-
Notifications
You must be signed in to change notification settings - Fork 757
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
to help with contract calls, especially invoking functions
- Loading branch information
Showing
13 changed files
with
33,222 additions
and
250 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
Large diffs are not rendered by default.
Oops, something went wrong.
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,31 @@ | ||
import fs from 'fs'; | ||
import { CompiledContract, Contract, deployContract, JsonParser, randomAddress } from '../src'; | ||
|
||
const compiledERC20: CompiledContract = JsonParser.parse( | ||
fs.readFileSync('./__mocks__/ERC20.json').toString('ascii') | ||
); | ||
|
||
describe('new Contract()', () => { | ||
const address = randomAddress(); | ||
// const address = ""; | ||
const wallet = randomAddress(); | ||
const contract = new Contract(compiledERC20.abi, address); | ||
beforeAll(async () => { | ||
const { code, tx_id } = await deployContract(compiledERC20, address); | ||
// I want to show the tx number to the tester, so he/she can trace the transaction in the explorer. | ||
// eslint-disable-next-line no-console | ||
console.log('deployed erc20 contract', tx_id); | ||
expect(code).toBe('TRANSACTION_RECEIVED'); | ||
}); | ||
test('initialize ERC20 mock contract', async () => { | ||
const response = await contract.invoke('mint', { | ||
recipient: wallet, | ||
amount: '10', | ||
}); | ||
expect(response.code).toBe('TRANSACTION_RECEIVED'); | ||
|
||
// I want to show the tx number to the tester, so he/she can trace the transaction in the explorer. | ||
// eslint-disable-next-line no-console | ||
console.log('txId:', response.tx_id, ', funded wallet:', wallet); | ||
}); | ||
}); |
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
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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,109 @@ | ||
import assert from 'assert'; | ||
import { BigNumber } from '@ethersproject/bignumber'; | ||
import { Abi } from './types'; | ||
import { getSelectorFromName } from './utils'; | ||
import { addTransaction } from './starknet'; | ||
|
||
type Args = { [inputName: string]: string | string[] }; | ||
type Calldata = string[]; | ||
|
||
const parseFelt = (candidate: string): BigNumber => { | ||
try { | ||
return BigNumber.from(candidate); | ||
} catch (e) { | ||
throw Error('Couldnt parse felt'); | ||
} | ||
}; | ||
|
||
const isFelt = (candidate: string): boolean => { | ||
try { | ||
parseFelt(candidate); | ||
return true; | ||
} catch (e) { | ||
return false; | ||
} | ||
}; | ||
|
||
export class Contract { | ||
connectedTo: string | null = null; | ||
|
||
abi: Abi[]; | ||
|
||
/** | ||
* Contract class to handle contract methods | ||
* | ||
* @param abi - Abi of the contract object | ||
* @param address (optional) - address to connect to | ||
*/ | ||
constructor(abi: Abi[], address: string | null = null) { | ||
this.connectedTo = address; | ||
this.abi = abi; | ||
} | ||
|
||
public connect(address: string): Contract { | ||
this.connectedTo = address; | ||
return this; | ||
} | ||
|
||
private static compileCalldata(args: Args): Calldata { | ||
return Object.values(args).flatMap((value) => { | ||
if (Array.isArray(value)) | ||
return [ | ||
BigNumber.from(value.length).toString(), | ||
...value.map((x) => BigNumber.from(x).toString()), | ||
]; | ||
return BigNumber.from(value).toString(); | ||
}); | ||
} | ||
|
||
public invoke(method: string, args: Args = {}) { | ||
// ensure contract is connected | ||
assert(this.connectedTo !== null, 'contract isnt connected to an address'); | ||
|
||
// ensure provided method exists | ||
const invokeableFunctionNames = this.abi | ||
.filter((abi) => { | ||
const isView = abi.stateMutability === 'view'; | ||
const isFunction = abi.type === 'function'; | ||
return isFunction && !isView; | ||
}) | ||
.map((abi) => abi.name); | ||
assert(invokeableFunctionNames.includes(method), 'invokeable method not found in abi'); | ||
|
||
// ensure args match abi type | ||
const methodAbi = this.abi.find((abi) => abi.name === method)!; | ||
methodAbi.inputs.forEach((input) => { | ||
assert(args[input.name] !== undefined, `no arg for "${input.name}" provided`); | ||
if (input.type === 'felt') { | ||
assert(typeof args[input.name] === 'string', `arg ${input.name} should be a felt (string)`); | ||
assert( | ||
isFelt(args[input.name] as string), | ||
`arg ${input.name} should be decimal or hexadecimal` | ||
); | ||
} else { | ||
assert(Array.isArray(args[input.name]), `arg ${input.name} should be a felt* (string[])`); | ||
(args[input.name] as string[]).forEach((felt, i) => { | ||
assert( | ||
typeof felt === 'string', | ||
`arg ${input.name}[${i}] should be a felt (string) as part of a felt* (string[])` | ||
); | ||
assert( | ||
isFelt(felt), | ||
`arg ${input.name}[${i}] should be decimal or hexadecimal as part of a felt* (string[])` | ||
); | ||
}); | ||
} | ||
}); | ||
|
||
// compile calldata | ||
const entrypointSelector = getSelectorFromName(method); | ||
const calldata = Contract.compileCalldata(args); | ||
|
||
return addTransaction({ | ||
type: 'INVOKE_FUNCTION', | ||
contract_address: this.connectedTo, | ||
calldata, | ||
entry_point_selector: entrypointSelector, | ||
}); | ||
} | ||
} |
Oops, something went wrong.