-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'feat/BCI-2538-update-examples' of github.com:smartcontr…
…actkit/chainlink-starknet into feat/BCI-2538-update-examples
- Loading branch information
Showing
5 changed files
with
186 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import { Account, RpcProvider, ec, uint256, constants } from 'starknet' | ||
|
||
export const ERC20_ADDRESS = '0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7' | ||
|
||
export const DEVNET_URL = 'http://127.0.0.1:5050' | ||
const DEVNET_NAME = 'devnet' | ||
// This function loads options from the environment. | ||
// It returns options for Devnet as default when nothing is configured in the environment. | ||
export const makeFunderOptsFromEnv = () => { | ||
const network = process.env.NETWORK || DEVNET_NAME | ||
const gateway = process.env.NODE_URL || DEVNET_URL | ||
const accountAddr = process.env.ACCOUNT?.toLowerCase() | ||
const keyPair = ec.starkCurve.utils.randomPrivateKey() | ||
|
||
return { network, gateway, accountAddr, keyPair } | ||
} | ||
|
||
interface FundAccounts { | ||
account: string | ||
amount: number | ||
} | ||
|
||
interface FunderOptions { | ||
network?: string | ||
gateway?: string | ||
accountAddr?: string | ||
keyPair?: Uint8Array | ||
} | ||
|
||
// Define the Strategy to use depending on the network. | ||
export class Funder { | ||
private opts: FunderOptions | ||
private strategy: IFundingStrategy | ||
|
||
constructor(opts: FunderOptions) { | ||
this.opts = opts | ||
if (this.opts.network === DEVNET_NAME) { | ||
this.strategy = new DevnetFundingStrategy() | ||
return | ||
} | ||
this.strategy = new AllowanceFundingStrategy() | ||
} | ||
|
||
// This function adds some funds to pre-deployed account that we are using in our test. | ||
public async fund(accounts: FundAccounts[]) { | ||
await this.strategy.fund(accounts, this.opts) | ||
} | ||
} | ||
|
||
interface IFundingStrategy { | ||
fund(accounts: FundAccounts[], opts: FunderOptions): Promise<void> | ||
} | ||
|
||
// Fund the Account on Devnet | ||
class DevnetFundingStrategy implements IFundingStrategy { | ||
public async fund(accounts: FundAccounts[], opts: FunderOptions) { | ||
accounts.forEach(async (account) => { | ||
const body = { | ||
address: account.account, | ||
amount: account.amount, | ||
lite: true, | ||
} | ||
await fetch(`${opts.gateway}/mint`, { | ||
method: 'post', | ||
body: JSON.stringify(body), | ||
headers: { 'Content-Type': 'application/json' }, | ||
}) | ||
}) | ||
} | ||
} | ||
|
||
// Fund the Account on Testnet | ||
class AllowanceFundingStrategy implements IFundingStrategy { | ||
public async fund(accounts: FundAccounts[], opts: FunderOptions) { | ||
const provider = new RpcProvider({ | ||
nodeUrl: constants.NetworkName.SN_GOERLI, | ||
}) | ||
|
||
const operator = new Account(provider, opts.accountAddr, opts.keyPair) | ||
|
||
for (const account of accounts) { | ||
const data = [ | ||
account.account, | ||
uint256.bnToUint256(account.amount).low.toString(), | ||
uint256.bnToUint256(account.amount).high.toString(), | ||
] | ||
const nonce = await operator.getNonce() | ||
const hash = await operator.execute( | ||
{ | ||
contractAddress: ERC20_ADDRESS, | ||
entrypoint: 'transfer', | ||
calldata: data, | ||
}, | ||
undefined, | ||
{ nonce }, | ||
) | ||
await provider.waitForTransaction(hash.transaction_hash) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import { expect } from 'chai' | ||
import { artifacts, network } from 'hardhat' | ||
|
||
// This function adds the build info to the test network so that the network knows | ||
// how to handle custom errors. It is automatically done when testing | ||
// against the default hardhat network. | ||
export const addCompilationToNetwork = async (fullyQualifiedName: string) => { | ||
if (network.name !== 'hardhat') { | ||
// This is so that the network can know about custom errors. | ||
// Running against the provided hardhat node does this automatically. | ||
|
||
const buildInfo = await artifacts.getBuildInfo(fullyQualifiedName) | ||
if (!buildInfo) { | ||
throw Error('Cannot find build info') | ||
} | ||
const { solcVersion, input, output } = buildInfo | ||
console.log('Sending compilation result for StarknetValidator test') | ||
await network.provider.request({ | ||
method: 'hardhat_addCompilationResult', | ||
params: [solcVersion, input, output], | ||
}) | ||
console.log('Successfully sent compilation result for StarknetValidator test') | ||
} | ||
} | ||
|
||
export const expectInvokeError = async (invoke: Promise<any>, expected?: string) => { | ||
try { | ||
await invoke | ||
} catch (err: any) { | ||
expectInvokeErrorMsg(err?.message, expected) | ||
return // force | ||
} | ||
expect.fail("Unexpected! Invoke didn't error!?") | ||
} | ||
|
||
export const expectInvokeErrorMsg = (actual: string, expected?: string) => { | ||
// Match transaction error | ||
expect(actual).to.deep.contain('TRANSACTION_FAILED') | ||
// Match specific error | ||
if (expected) expectSpecificMsg(actual, expected) | ||
} | ||
|
||
export const expectCallError = async (call: Promise<any>, expected?: string) => { | ||
try { | ||
await call | ||
} catch (err: any) { | ||
expectCallErrorMsg(err?.message, expected) | ||
return // force | ||
} | ||
expect.fail("Unexpected! Call didn't error!?") | ||
} | ||
|
||
export const expectCallErrorMsg = (actual: string, expected?: string) => { | ||
// Match call error | ||
expect(actual).to.deep.contain('Could not perform call') | ||
// Match specific error | ||
if (expected) expectSpecificMsg(actual, expected) | ||
} | ||
|
||
export const expectSpecificMsg = (actual: string, expected: string) => { | ||
// The error message is displayed as a felt hex string, so we need to convert the text. | ||
// ref: https://github.com/starkware-libs/cairo-lang/blob/c954f154bbab04c3fb27f7598b015a9475fc628e/src/starkware/starknet/business_logic/execution/execute_entry_point.py#L223 | ||
const expectedHex = '0x' + Buffer.from(expected, 'utf8').toString('hex') | ||
const errorMessage = `Execution was reverted; failure reason: [${expectedHex}]` | ||
if (!actual.includes(errorMessage)) { | ||
expect.fail(`\nActual: ${actual}\n\nExpected:\n\tFelt hex: ${expectedHex}\n\tText: ${expected}`) | ||
} | ||
} | ||
|
||
// Starknet v0.11.0 and higher only allow declaring a class once: | ||
// https://github.com/starkware-libs/starknet-specs/pull/85 | ||
export const expectSuccessOrDeclared = async (declareContractPromise: Promise<any>) => { | ||
try { | ||
await declareContractPromise | ||
} catch (err: any) { | ||
if (/Class with hash 0x[0-9a-f]+ is already declared\./.test(err?.message)) { | ||
return // force | ||
} | ||
expect.fail(err) | ||
} | ||
} |