-
Notifications
You must be signed in to change notification settings - Fork 77
Expand file tree
/
Copy pathbridge_erc721.ts
More file actions
276 lines (236 loc) · 8.78 KB
/
bridge_erc721.ts
File metadata and controls
276 lines (236 loc) · 8.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
import type { NFT } from '@fuel-bridge/solidity-contracts/typechain';
import type { TestEnvironment } from '@fuel-bridge/test-utils';
import {
setupEnvironment,
relayCommonMessage,
waitForMessage,
createRelayMessageParams,
getOrDeployL2Bridge,
FUEL_TX_PARAMS,
getMessageOutReceipt,
fuel_to_eth_address,
waitForBlockCommit,
waitForBlockFinalization,
getTokenId,
getBlock,
getOrDeployERC721Contract,
FUEL_CALL_TX_PARAMS,
} from '@fuel-bridge/test-utils';
import chai from 'chai';
import { zeroPadValue } from 'ethers';
import type { Signer } from 'ethers';
import { Address, BN } from 'fuels';
import type {
AbstractAddress,
Contract,
WalletUnlocked as FuelWallet,
MessageProof,
} from 'fuels';
import { startContainers, stopEnvironment } from '../docker-setup/docker';
const { expect } = chai;
// TODO: develop new version of ERC721 gateway
describe.skip('Bridging ERC721 tokens', async function () {
// Timeout 6 minutes 40 seconds
const DEFAULT_TIMEOUT_MS: number = 400_000;
const FUEL_MESSAGE_TIMEOUT_MS: number = 30_000;
let env: TestEnvironment;
let eth_testToken: NFT;
let eth_testTokenAddress: string;
let eth_tokenId: string;
let eth_erc721GatewayAddress: string;
let fuel_testToken: Contract;
let fuel_testContractId: string;
let fuel_testAssetId: string;
// override the default test timeout from 2000ms
this.timeout(DEFAULT_TIMEOUT_MS);
before(async () => {
// spinning up all docker containers
await startContainers();
env = await setupEnvironment({});
eth_testToken = await getOrDeployERC721Contract(env);
eth_testTokenAddress = (await eth_testToken.getAddress()).toLowerCase();
eth_erc721GatewayAddress = (
await env.eth.fuelERC721Gateway.getAddress()
).toLowerCase();
const { contract } = await getOrDeployL2Bridge(
env,
env.eth.fuelERC721Gateway
);
fuel_testToken = contract;
fuel_testContractId = fuel_testToken.id.toHexString();
const { value: expectedTokenContractId } = await fuel_testToken.functions
.bridged_token()
.txParams(FUEL_CALL_TX_PARAMS)
.dryRun();
const { value: expectedGatewayContractId } = await fuel_testToken.functions
.bridged_token_gateway()
.txParams(FUEL_CALL_TX_PARAMS)
.dryRun();
// check that values for the test token and gateway contract match what
// was compiled into the bridge-fungible-token binaries
expect(fuel_to_eth_address(expectedTokenContractId)).to.equal(
eth_testTokenAddress
);
expect(fuel_to_eth_address(expectedGatewayContractId)).to.equal(
eth_erc721GatewayAddress
);
// mint tokens as starting balances
const deployerAddr = await env.eth.deployer.getAddress();
await eth_testToken.mint(deployerAddr, deployerAddr);
const signer0Addr = await env.eth.signers[0].getAddress();
await eth_testToken.mint(signer0Addr, signer0Addr);
const signer1Addr = await env.eth.signers[1].getAddress();
await eth_testToken.mint(signer1Addr, signer1Addr);
});
describe('Bridge ERC721 to Fuel', async () => {
let ethereumTokenSender: Signer;
let fuelTokenReceiver: FuelWallet;
let fuelTokenReceiverAddress: string;
let fuelTokenMessageNonce: BN;
let fuelTokenMessageReceiver: AbstractAddress;
before(async () => {
ethereumTokenSender = env.eth.signers[0];
fuelTokenReceiver = env.fuel.signers[0];
fuelTokenReceiverAddress = fuelTokenReceiver.address.toHexString();
eth_tokenId = zeroPadValue(await ethereumTokenSender.getAddress(), 32);
fuel_testAssetId = getTokenId(fuel_testToken, eth_tokenId);
});
it('Bridge ERC721 via FuelERC721Gateway', async () => {
// approve FuelERC721Gateway to spend the tokens
await eth_testToken
.connect(ethereumTokenSender)
.approve(env.eth.fuelERC721Gateway, eth_tokenId);
// use the FuelERC721Gateway to deposit test tokens and receive equivalent tokens on Fuel
const receipt = await env.eth.fuelERC721Gateway
.connect(ethereumTokenSender)
.deposit(
fuelTokenReceiverAddress,
eth_testTokenAddress,
fuel_testContractId,
eth_tokenId
)
.then((tx) => tx.wait());
expect(receipt.status).to.equal(1);
const filter = env.eth.fuelMessagePortal.filters.MessageSent(
null, // Args set to null since there should be just 1 event for MessageSent
null,
null,
null,
null
);
const [event, ...restOfEvents] =
await env.eth.fuelMessagePortal.queryFilter(
filter,
receipt.blockNumber,
receipt.blockNumber
);
expect(restOfEvents.length).to.be.eq(0); // Should be only 1 event
// parse events from logs
fuelTokenMessageNonce = new BN(event.args.nonce.toString());
fuelTokenMessageReceiver = Address.fromB256(event.args.recipient);
// check that the tokenId now belongs to the gateway
expect(
(await eth_testToken.ownerOf(eth_tokenId)).toLowerCase()
).to.be.equal(eth_erc721GatewayAddress);
});
it('Relay message from Ethereum on Fuel', async function () {
// override the default test timeout from 2000ms
this.timeout(FUEL_MESSAGE_TIMEOUT_MS);
// relay the message ourselves
const message = await waitForMessage(
env.fuel.provider,
fuelTokenMessageReceiver,
fuelTokenMessageNonce,
FUEL_MESSAGE_TIMEOUT_MS
);
expect(message).to.not.be.null;
const tx = await relayCommonMessage(env.fuel.deployer, message, {
...FUEL_TX_PARAMS,
maturity: undefined,
});
const result = await tx.waitForResult();
expect(result.status).to.equal('success');
});
it('Check ERC721 arrived on Fuel', async () => {
// check that the recipient balance has increased by the expected amount
const balance = await fuelTokenReceiver.getBalance(fuel_testAssetId);
expect(balance.toNumber()).to.be.eq(1);
});
});
describe('Bridge ERC721 from Fuel', async () => {
let fuelTokenSender: FuelWallet;
let ethereumTokenReceiver: Signer;
let ethereumTokenReceiverAddress: string;
let withdrawMessageProof: MessageProof;
before(async () => {
fuelTokenSender = env.fuel.signers[0];
ethereumTokenReceiver = env.eth.signers[1];
ethereumTokenReceiverAddress = await ethereumTokenReceiver.getAddress();
});
it('Bridge ERC721 via Fuel token contract', async () => {
// withdraw tokens back to the base chain
fuel_testToken.account = fuelTokenSender;
const paddedAddress =
'0x' + ethereumTokenReceiverAddress.slice(2).padStart(64, '0');
const transactionRequest = await fuel_testToken.functions
.withdraw(paddedAddress)
.txParams(FUEL_CALL_TX_PARAMS)
.callParams({
forward: {
amount: 1,
assetId: fuel_testAssetId,
},
})
.fundWithRequiredCoins();
const tx = await fuelTokenSender.sendTransaction(transactionRequest);
const fWithdrawTxResult = await tx.waitForResult();
expect(fWithdrawTxResult.status).to.equal('success');
// Wait for the commited block
const withdrawBlock = await getBlock(
env.fuel.provider.url,
fWithdrawTxResult.blockId
);
const commitHashAtL1 = await waitForBlockCommit(
env,
withdrawBlock.header.height
);
const messageOutReceipt = getMessageOutReceipt(
fWithdrawTxResult.receipts
);
withdrawMessageProof = await fuelTokenSender.provider.getMessageProof(
tx.id,
messageOutReceipt.nonce,
commitHashAtL1
);
// check that the sender balance has decreased by the expected amount
const newSenderBalance = await fuelTokenSender.getBalance(
fuel_testAssetId
);
expect(newSenderBalance.toNumber()).to.be.eq(0);
});
it('Relay Message from Fuel on Ethereum', async () => {
// wait for block finalization
await waitForBlockFinalization(env, withdrawMessageProof);
// construct relay message proof data
const relayMessageParams = createRelayMessageParams(withdrawMessageProof);
// relay message
await env.eth.fuelMessagePortal.relayMessage(
relayMessageParams.message,
relayMessageParams.rootBlockHeader,
relayMessageParams.blockHeader,
relayMessageParams.blockInHistoryProof,
relayMessageParams.messageInBlockProof
);
});
it('Check ERC721 arrived on Ethereum', async () => {
// check that the recipient balance has increased by the expected amount
expect(await eth_testToken.ownerOf(eth_tokenId)).to.be.equal(
ethereumTokenReceiverAddress
);
});
});
// stopping containers post the test
after(async () => {
await stopEnvironment();
});
});