Skip to content

Commit dd09bf0

Browse files
committed
docs: dded code examples for Contract (ethers-io#982).
1 parent 4b163e9 commit dd09bf0

File tree

2 files changed

+272
-22
lines changed

2 files changed

+272
-22
lines changed

docs.wrm/api/contract/MyToken.sol

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Do not use this; it is only for an example in the docs
2+
3+
contract MyToken {
4+
event Transfer(address indexed from, address indexed to, uint amount);
5+
6+
mapping (address => uint256) _balances;
7+
8+
constructor(uint256 totalSupply) {
9+
emit Transfer(address(0), msg.sender, totalSupply);
10+
_balances[msg.sender] = totalSupply;
11+
}
12+
13+
// Read-Only Functions
14+
function balanceOf(address owner) public view returns (uint256) {
15+
return _balances[owner];
16+
}
17+
18+
function decimals() public pure returns (uint8) {
19+
return 18;
20+
}
21+
22+
function symbol() public pure returns (string memory) {
23+
return "MyToken";
24+
}
25+
26+
// Authenticated Functions
27+
function transfer(address to, uint amount) public returns (bool) {
28+
require(_balances[msg.sender] >= amount, "insufficient token balance");
29+
_balances[msg.sender] -= amount;
30+
_balances[to] += amount;
31+
emit Transfer(msg.sender, to, amount);
32+
return true;
33+
}
34+
}

docs.wrm/api/contract/example.wrm

Lines changed: 238 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,91 @@
11
_section: Example: ERC-20 Contract
22

3+
The concept of Meta-Classes is somewhat confusing, so we will go
4+
over a short example.
5+
6+
A meta-class is a class which is defined at run-time. A Contract
7+
is specified by an //Application Binary Interface// (ABI), which describes
8+
the methods and events it has. This description is passed the the
9+
[[Contract]] object at run-time, and it creates a new Class, adding
10+
all the methods defined in the ABI at run-time.
11+
12+
_subsection: Deploying a Contract
13+
14+
Most often, any contract you will need to interact with will already
15+
be deployed to the blockchain, but for this example will will first
16+
deploy the contract.
17+
18+
_property: new ethers.ContractFactory(abi, bytecode, signer)
19+
Create a new [[ContractFactory]] which can deploy a contract to the
20+
blockchain.
21+
22+
_code: @lang<javascript>
23+
24+
//_hide: const signer = localSigner;
25+
//_hide: const parseUnits = utils.parseUnits;
26+
27+
const bytecode = "0x608060405234801561001057600080fd5b506040516103bc3803806103bc83398101604081905261002f9161007c565b60405181815233906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a333600090815260208190526040902055610094565b60006020828403121561008d578081fd5b5051919050565b610319806100a36000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c8063313ce5671461005157806370a082311461006557806395d89b411461009c578063a9059cbb146100c5575b600080fd5b604051601281526020015b60405180910390f35b61008e610073366004610201565b6001600160a01b031660009081526020819052604090205490565b60405190815260200161005c565b604080518082018252600781526626bcaa37b5b2b760c91b6020820152905161005c919061024b565b6100d86100d3366004610222565b6100e8565b604051901515815260200161005c565b3360009081526020819052604081205482111561014b5760405162461bcd60e51b815260206004820152601a60248201527f696e73756666696369656e7420746f6b656e2062616c616e6365000000000000604482015260640160405180910390fd5b336000908152602081905260408120805484929061016a9084906102b6565b90915550506001600160a01b0383166000908152602081905260408120805484929061019790849061029e565b90915550506040518281526001600160a01b0384169033907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a350600192915050565b80356001600160a01b03811681146101fc57600080fd5b919050565b600060208284031215610212578081fd5b61021b826101e5565b9392505050565b60008060408385031215610234578081fd5b61023d836101e5565b946020939093013593505050565b6000602080835283518082850152825b818110156102775785810183015185820160400152820161025b565b818111156102885783604083870101525b50601f01601f1916929092016040019392505050565b600082198211156102b1576102b16102cd565b500190565b6000828210156102c8576102c86102cd565b500390565b634e487b7160e01b600052601160045260246000fdfea2646970667358221220d80384ce584e101c5b92e4ee9b7871262285070dbcd2d71f99601f0f4fcecd2364736f6c63430008040033";
28+
29+
// A Human-Readable ABI; we only need to specify relevant fragments,
30+
// in the case of deployment this means the constructor
31+
const abi = [
32+
"constructor(uint totalSupply)"
33+
];
34+
35+
const factory = new ethers.ContractFactory(abi, bytecode, signer)
36+
37+
// Deploy, setting total supply to 100 tokens (assigned to the deployer)
38+
const contract = await factory.deploy(parseUnits("100"));
39+
40+
// The contract is not currentl live on the network yet, however
41+
// its address is ready for us
42+
//_result:
43+
contract.address
44+
//_log:
45+
//_hide: _page.address = contract.address;
46+
47+
// Wait until the contract has been deployed before interacting
48+
// with it; returns the receipt for the deployemnt transaction
49+
//_result:
50+
await contract.deployTransaction.wait();
51+
//_log:
52+
53+
354
_subsection: Connecting to a Contract
455

5-
_code: A simple ERC-20 contract @lang<javascript>
56+
_heading: ERC20Contract @INHERIT<[[Contract]]>
57+
58+
_property: new ethers.Contract(address, abi, providerOrSigner)
59+
Creating a new instance of a Contract connects to an existing
60+
contract by specifying its //address// on the blockchain,
61+
its //abi// (used to populate the class' methods) a //providerOrSigner//.
62+
63+
If a [[Provider]] is given, the contract has only read-only access, while
64+
a [[Signer]] offers access to state manipulating methods.
65+
66+
_code: @lang<javascript>
667

7-
// A Human-Readable ABI; any supported ABI format could be used
68+
//_hide: const provider = localProvider;
69+
//_hide: const signer = localSigner;
70+
71+
// A Human-Readable ABI; for interacting with the contract, we
72+
// must include any fragment we wish to use
873
const abi = [
974
// Read-Only Functions
1075
"function balanceOf(address owner) view returns (uint256)",
1176
"function decimals() view returns (uint8)",
1277
"function symbol() view returns (string)",
1378

1479
// Authenticated Functions
15-
"function transfer(address to, uint amount) returns (boolean)",
80+
"function transfer(address to, uint amount) returns (bool)",
1681

1782
// Events
1883
"event Transfer(address indexed from, address indexed to, uint amount)"
1984
];
2085

2186
// This can be an address or an ENS name
22-
const address = "dai.tokens.ethers.eth";
23-
24-
// An example Provider
25-
const provider = ethers.getDefaultProvider();
26-
27-
// An example Signer
28-
const signer = ethers.Wallet.createRandom().connect(provider);
87+
//_hide: const address = _page.address;
88+
//_verbatim: `const address = ${ JSON.stringify(address) };`
2989

3090
// Read-Only; By connecting to a Provider, allows:
3191
// - Any constant function
@@ -34,20 +94,13 @@ const signer = ethers.Wallet.createRandom().connect(provider);
3494
// - Estimating Gas for non-constant (as an anonymous sender)
3595
// - Static Calling non-constant methods (as anonymous sender)
3696
const erc20 = new ethers.Contract(address, abi, provider);
97+
//_hide: _page.erc20 = erc20;
3798

3899
// Read-Write; By connecting to a Signer, allows:
39100
// - Everything from Read-Only (except as Signer, not anonymous)
40101
// - Sending transactions for non-constant functions
41-
const erc20_rw = new ethers.Contract(address, abi, signer)
42-
43-
44-
_heading: ERC20Contract @INHERIT<[[Contract]]>
45-
46-
_property: new ethers.Contract(address, abi, providerOrSigner)
47-
See the above code example for creating an Instance which will
48-
(in addition to the Contact methods and properties) automatically
49-
add the additional properties defined in //abi// to a **Contract**
50-
connected to //address// using the //providerOrSigner//.
102+
const erc20_rw = new ethers.Contract(address, abi, signer);
103+
//_hide: _page.erc20_rw = erc20_rw;
51104

52105

53106
_subsection: Properties @NOTE<(inheritted from [[Contract]])>
@@ -101,6 +154,8 @@ _property: Contract.isIndexed(value) => boolean
101154

102155
_subsection: Events @NOTE<(inheritted from [[Contract]])> @<erc20-events>
103156

157+
See [Meta-Class Filters](erc20-meta-events) for examples using events.
158+
104159
_property: erc20.queryFilter(event [ , fromBlockOrBlockHash [ , toBlock ]) => Promise<Array<Event>> @<erc20-queryfilter>
105160
Return Events that match the //event//.
106161

@@ -126,7 +181,7 @@ Unsubscribe all listeners for //event//. If no event is provided,
126181
all events are unsubscribed.
127182

128183

129-
_subsection: Meta-Class Methods @NOTE<(added at Runtime)>
184+
_subsection: Meta-Class Methods @NOTE<(added at Runtime)> @<erc20-meta-methods>
130185

131186
Since the Contract is a Meta-Class, the methods available here depend
132187
on the ABI which was passed into the **Contract**.
@@ -136,33 +191,122 @@ Returns the number of decimal places used by this ERC-20 token. This can be
136191
used with [parseUnits](utils-parseUnits) when taking input from the user or
137192
[formatUnits](utils-formatunits] when displaying the token amounts in the UI.
138193

194+
_code: @lang<javascript>
195+
196+
//_hide: const erc20 = _page.erc20;
197+
//_result:
198+
await erc20.decimals();
199+
//_log:
200+
139201
_property: erc20.balanceOf(owner [, overrides ]) => Promise<[[BigNumber]]>
140202
Returns the balance of //owner// for this ERC-20 token.
141203

204+
_code: @lang<javascript>
205+
206+
//_hide: const signer = localSigner;
207+
//_hide: const erc20 = _page.erc20;
208+
209+
//_result:
210+
await erc20.balanceOf(signer.getAddress())
211+
//_log:
212+
142213
_property: erc20.symbol([ overrides ]) => Promise<string>
143214
Returns the symbol of the token.
144215

216+
_code: @lang<javascript>
217+
218+
//_hide: const erc20 = _page.erc20;
219+
//_result:
220+
await erc20.symbol();
221+
//_log:
222+
145223
_property: erc20_rw.transfer(target, amount [, overrides ]) => Promise<[[providers-TransactionResponse]]>
146224
Transfers //amount// tokens to //target// from the current signer.
147225
The return value (a boolean) is inaccessible during a write operation
148226
using a transaction. Other techniques (such as events) are required
149227
if this value is required. On-chain contracts calling the ``transfer``
150228
function have access to this result, which is why it is possible.
151229

230+
_code: @lang<javascript>
231+
232+
//_hide: const signer = localSigner;
233+
//_hide: const erc20_rw = _page.erc20_rw;
234+
//_hide: const parseUnits = utils.parseUnits;
235+
//_hide: const formatUnits = utils.formatUnits;
236+
237+
// Before...
238+
//_result:
239+
formatUnits(await erc20_rw.balanceOf(signer.getAddress()));
240+
//_log:
241+
242+
// Transfer 1.23 tokens to the ENS name "ricmoo.eth"
243+
//_result:
244+
tx = await erc20_rw.transfer("ricmoo.eth", parseUnits("1.23"));
245+
//_log:
246+
247+
// Wait for the transaction to be mined...
248+
//_result:
249+
await tx.wait();
250+
//_log:
251+
252+
// After!
253+
//_result:
254+
formatUnits(await erc20_rw.balanceOf(signer.getAddress()));
255+
//_log:
256+
257+
//_result:
258+
formatUnits(await erc20_rw.balanceOf("ricmoo.eth"));
259+
//_log:
260+
152261
_property: erc20.callStatic.transfer(target, amount [, overrides ]) => Promise<boolean>
153262
Performs a dry-run of transferring //amount// tokens to //target// from
154263
the current signer, without actually signing or sending a transaction.
155264

156265
This can be used to preflight check that a transaction will be successful.
157266

267+
_code: @lang<javascript>
268+
269+
//_hide: const erc20_rw = _page.erc20_rw;
270+
//_hide: const randomWallet = ethers.Wallet.createRandom().connect(erc20_rw.provider);
271+
//_hide: const parseUnits = utils.parseUnits;
272+
273+
// The signer has enough tokens to send, so true is returned
274+
//_result:
275+
await erc20_rw.callStatic.transfer("ricmoo.eth", parseUnits("1.23"));
276+
//_log:
277+
278+
// A random address does not have enough tokens to
279+
// send, in which case the contract throws an error
280+
erc20_random = erc20_rw.connect(randomWallet);
281+
//_throws:
282+
await erc20_random.callStatic.transfer("ricmoo.eth", parseUnits("1.23"));
283+
//_log:
284+
158285
_property: erc20.estimateGas.transfer(target, amount [, overrides ]) => Promise<[[BigNumber]]>
159286
Returns an estimate for how many units of gas would be required
160287
to transfer //amount// tokens to //target//.
161288

289+
_code: @lang<javascript>
290+
291+
//_hide: const erc20_rw = _page.erc20_rw;
292+
//_hide: const parseUnits = utils.parseUnits;
293+
294+
//_result:
295+
await erc20_rw.estimateGas.transfer("ricmoo.eth", parseUnits("1.23"));
296+
//_log:
297+
162298
_property: erc20.populateTransaction.transfer(target, amount [, overrides ]) => Promise<[UnsignedTx](UnsignedTransaction)>
163299
Returns an [[UnsignedTransaction]] which could be signed and submitted
164300
to the network to transaction //amount// tokens to //target//.
165301

302+
_code: @lang<javascript>
303+
304+
//_hide: const erc20_rw = _page.erc20_rw;
305+
//_hide: const parseUnits = utils.parseUnits;
306+
307+
//_result:
308+
await erc20_rw.populateTransaction.transfer("ricmoo.eth", parseUnits("1.23"));
309+
//_log:
166310

167311
_note: Note on Estimating and Static Calling
168312

@@ -173,7 +317,7 @@ blockchain also means there are certain consistency modes that cannot be
173317
known until an actual transaction is attempted.
174318

175319

176-
_subsection: Meta-Class Filters @NOTE<(added at Runtime)>
320+
_subsection: Meta-Class Filters @NOTE<(added at Runtime)> @<erc20-meta-events>
177321

178322
Since the Contract is a Meta-Class, the methods available here depend
179323
on the ABI which was passed into the **Contract**.
@@ -184,3 +328,75 @@ to [subscribe/unsubscribe to events](erc20-events).
184328

185329
If //fromAddress// is null or not provided, then any from address matches.
186330
If //toAddress// is null or not provided, then any to address matches.
331+
332+
_code: query filter *from* events @lang<javascript>
333+
334+
//_hide: const signer = localSigner;
335+
//_hide: const erc20 = _page.erc20;
336+
337+
//_result:
338+
filterFrom = erc20.filters.Transfer(signer.address);
339+
//_log:
340+
341+
// Search for transfers *from* me in the last 10 blocks
342+
//_result:
343+
logsFrom = await erc20.queryFilter(filterFrom, -10, "latest");
344+
//_log:
345+
346+
// Note that the args providees the details of the event, each
347+
// parameters is available positionally, and since our ABI
348+
// included parameter names also by name
349+
//_result:
350+
logsFrom[0].args
351+
//_log:
352+
353+
//_hide: _page.filterFrom = filterFrom;
354+
355+
356+
_code: query filter with *to* events @lang<javascript>
357+
358+
//_hide: const signer = localSigner;
359+
//_hide: const erc20 = _page.erc20;
360+
361+
//_result:
362+
filterTo = erc20.filters.Transfer(null, signer.address);
363+
//_log:
364+
365+
// Search for transfers *to* me in the last 10 blocks
366+
// Note: the contract transferred totalSupply tokens to us
367+
// when it was deployed in its constructor
368+
//_result:
369+
logsTo = await erc20.queryFilter(filterTo, -10, "latest");
370+
//_log:
371+
372+
// Note that the args providees the details of the event, each
373+
// parameters is available positionally, and since our ABI
374+
// included parameter names also by name
375+
//_result:
376+
logsTo[0].args
377+
//_log:
378+
379+
//_hide: _page.filterTo = filterTo;
380+
381+
_code: listen for events @lang<javascript>
382+
383+
//_hide: const erc20 = _page.erc20;
384+
//_hide: const filterFrom = _page.filterFrom;
385+
//_hide: const filterTo = _page.filterTo;
386+
387+
// Listen to incoming events from signer:
388+
erc20.on(filterFrom, (from, to, amount, event) => {
389+
// The `from` will always be the signer address
390+
});
391+
392+
// Listen to incoming events to signer:
393+
erc20.on(filterTo, (from, to, amount, event) => {
394+
// The `to` will always be the signer address
395+
});
396+
397+
// Listen to all Transfer events:
398+
erc20.on("Transfer", (from, to, amount, event) => {
399+
// ...
400+
});
401+
402+
//_hide: erc20.removeAllListeners();

0 commit comments

Comments
 (0)