Add Registry, startExitWithBurntERC20Tokens
MODIFY
src/common/ContractsBase.ts
src/common/ContractsBase.ts
import Web3Client from './Web3Client'
+import BN from 'bn.js'
+import assert from 'assert';
export default class ContractsBase {
web3Client: Web3Client
@@ -6,4 +8,13 @@ export default class ContractsBase {
constructor(web3Client: Web3Client) {
this.web3Client = web3Client
}
+
+ public encode(number: BN | string) {
+ if (BN.isBN(number)) {
+ return '0x' + number.toString(16)
+ } else if (typeof number === 'string') {
+ assert.equal(number.slice(0, 2), '0x', 'expected a 0x prefixed string')
+ return number
+ }
+ }
}

MODIFY
src/common/Web3Client.ts
src/common/Web3Client.ts
@@ -14,18 +14,46 @@ export default class Web3Client {
this.maticDefaultOptions = maticDefaultOptions
}
- async send(method, options) {
+ async call(method, options?: SendOptions) {
+ return method.call(options || this.parentDefaultOptions)
+ }
+
+ async send(method, options?) {
const _options = options || this.parentDefaultOptions
// since we use the delegated proxy patterns, the following should be a good way to provide enough gas
// apparently even when provided with a buffer of 20k, the call reverts. This shouldn't be happening because the actual gas used is less than what the estimation returns
// providing higher buffer for now
- _options.gas = (await method.estimateGas()) + 200000
+ if (!_options.from) return new Error('from is not specified')
+ console.log(_options)
+ // _options.gas = (await method.estimateGas()) + 200000
return method.send(_options)
}
- async sendOnMatic(method, options) {
+ async callOnMatic(method, options?) {
+ return method.call(options || this.maticDefaultOptions)
+ }
+
+ async sendOnMatic(method, options?) {
const _options = options || this.maticDefaultOptions
- _options.gas = await method.estimateGas()
+ if (!_options.from) return new Error('from is not specified')
+ console.log(_options)
+ // _options.gas = await method.estimateGas()
return method.send(_options)
}
+
+ getParentWeb3() {
+ return this.parentWeb3
+ }
+
+ getMaticWeb3() {
+ return this.web3
+ }
+
+ setParentDefaultOptions(options: any) {
+ this.parentDefaultOptions = options
+ }
+
+ setMaticDefaultOptions(options: any) {
+ this.maticDefaultOptions = options
+ }
}

MODIFY
src/index.ts
src/index.ts
import DepositManager from './root/DepositManager'
import RootChain from './root/RootChain'
+import Registry from './root/Registry'
import WithdrawManager from './root/WithdrawManager'
import Web3Client from './common/Web3Client'
@@ -8,11 +9,24 @@ export default class Matic {
public depositManager: DepositManager
public rootChain: RootChain
public withdrawManager: WithdrawManager
+ public registry: Registry
constructor(options: any = {}) {
- this.web3Client = new Web3Client(options.parentProvider, options.maticProvider, options.parentDefaultOptions, options.maticDefaultOptions)
+ this.web3Client = new Web3Client(options.parentProvider, options.maticProvider, options.parentDefaultOptions || {}, options.maticDefaultOptions || {})
+ this.registry = new Registry(options.registry, this.web3Client)
this.rootChain = new RootChain(options.rootChain, this.web3Client)
- this.depositManager = new DepositManager(options.depositManagerAddress, this.web3Client)
- this.withdrawManager = new WithdrawManager(options.withdrawManager, this.rootChain, this.web3Client)
+ this.depositManager = new DepositManager(options.depositManager, this.web3Client)
+ this.withdrawManager = new WithdrawManager(options.withdrawManager, this.rootChain, this.web3Client, this.registry)
+ }
+
+ initialize() {
+ return Promise.all([
+ this.withdrawManager.initialize()
+ ])
}
}
+
+// export default async function initialize(options) {
+// const matic = new Matic(options)
+// return matic.initialize()
+// }

MODIFY
src/root/DepositManager.ts
src/root/DepositManager.ts
-import assert from 'assert';
import BN from 'bn.js'
import Contract from 'web3/eth/contract'
import DepositManagerArtifact from 'matic-protocol/contracts-core/artifacts/DepositManager.json'
@@ -60,13 +59,4 @@ export default class DepositManager extends ContractsBase {
getAddress() {
return this.depositManagerContract.options.address
}
-
- private encode(number: BN | string) {
- if (BN.isBN(number)) {
- return '0x' + number.toString(16)
- } else if (typeof number === 'string') {
- assert.equal(number.slice(0, 2), '0x', 'expected a 0x prefixed string')
- return number
- }
- }
}

ADD
src/root/Registry.ts
+import BN from 'bn.js'
+import Contract from 'web3/eth/contract'
+import RegistryArtifact from 'matic-protocol/contracts-core/artifacts/Registry.json'
+
+import ContractsBase from '../common/ContractsBase'
+import { address, SendOptions } from '../types/Common'
+import Web3Client from '../common/Web3Client'
+
+export default class DepositManager extends ContractsBase {
+ public registry: Contract
+
+ constructor(registry: address, web3Client: Web3Client) {
+ super(web3Client)
+ console.log('registry', registry)
+ this.registry = new this.web3Client.parentWeb3.eth.Contract(RegistryArtifact.abi, registry)
+ }
+}

MODIFY
src/root/RootChain.ts
src/root/RootChain.ts
@@ -4,6 +4,8 @@ import RootChainArtifact from 'matic-protocol/contracts-core/artifacts/RootChain
import ContractsBase from '../common/ContractsBase'
import { address } from '../types/Common'
import Web3Client from '../common/Web3Client'
+import Proofs from 'matic-protocol/contracts-core/helpers/proofs.js'
+import BN from 'bn.js'
export default class RootChain extends ContractsBase {
public rootChain: Contract
@@ -12,4 +14,47 @@ export default class RootChain extends ContractsBase {
super(web3Client)
this.rootChain = new this.web3Client.parentWeb3.eth.Contract(RootChainArtifact.abi, rootChain)
}
+
+ getLastChildBlock() {
+ return this.web3Client.call(this.rootChain.methods.getLastChildBlock())
+ }
+
+ async findHeaderBlockNumber(childBlockNumber: BN | string | number): Promise<BN> {
+ childBlockNumber = new BN(childBlockNumber)
+ let start = new BN(1)
+ let end = new BN(await this.web3Client.call(this.rootChain.methods.currentHeaderBlock())).div(new BN(10000))
+ console.log(start.toString(), end.toString())
+ let ans
+ while(start <= end) {
+ if (start == end) { ans = start; break; }
+ let mid = start.add(end).div(new BN(2))
+ // if (!mid.mod(new BN('10000')).eq(new BN(0))) mid.sub(new BN('5000'))
+ console.log('mid', mid.toString())
+ const headerBlock = await this.web3Client.call(this.rootChain.methods.headerBlocks(mid.mul(new BN(10000)).toString()))
+ const headerStart = new BN(headerBlock.start)
+ const headerEnd = new BN(headerBlock.end)
+ if (headerStart.lte(childBlockNumber) && childBlockNumber.lte(headerEnd)) {
+ ans = mid
+ break
+ } else if (headerStart.gt(childBlockNumber)) {
+ end = mid
+ } else if (headerEnd.lt(childBlockNumber)) {
+ start = mid
+ }
+ }
+ return ans.mul(new BN(10000))
+ // return new BN(851).mul(new BN(10000))
+ }
+
+ getRawContract() {
+ return this.rootChain
+ }
+
+ // async submitCheckpoint(start, end) {
+ // const root = await Proofs.buildCheckpointRoot(this.web3Client.getMaticWeb3(), start, end)
+ // const validators = await Proofs.getWalletFromMnemonic()
+ // this.web3Client.send(
+ // this.rootChain
+ // )
+ // }
}

MODIFY
src/root/WithdrawManager.ts
src/root/WithdrawManager.ts
@@ -4,30 +4,101 @@ import { address, SendOptions } from '../types/Common'
import WithdrawManagerArtifact from 'matic-protocol/contracts-core/artifacts/WithdrawManager.json'
import Proofs from 'matic-protocol/contracts-core/helpers/proofs.js'
import ChildERC20Artifact from 'matic-protocol/contracts-core/artifacts/ChildERC20.json'
+import ERC20PredicateArtifact from 'matic-protocol/contracts-core/artifacts/ERC20Predicate.json'
import BN from 'bn.js'
import Web3Client from '../common/Web3Client'
import ContractsBase from '../common/ContractsBase'
import RootChain from './RootChain'
+import assert from 'assert'
+import ethUtils from 'ethereumjs-util'
+import Registry from './Registry'
export default class WithdrawManager extends ContractsBase {
public withdrawManager: Contract
+ public erc20Predicate: Contract
private rootChain: RootChain
+ private registry: Registry
- constructor(withdrawManager: address, rootChain: RootChain, web3Client: Web3Client) {
+ constructor(withdrawManager: address, rootChain: RootChain, web3Client: Web3Client, registry: Registry) {
super(web3Client)
this.withdrawManager = new this.web3Client.parentWeb3.eth.Contract(WithdrawManagerArtifact.abi, withdrawManager)
this.rootChain = rootChain
+ this.registry = registry
+ }
+
+ async initialize() {
+ const erc20PredicateAddress = await this.registry.registry.methods.erc20Predicate().call()
+ console.log('erc20PredicateAddress', erc20PredicateAddress)
+ this.erc20Predicate = new this.web3Client.parentWeb3.eth.Contract(ERC20PredicateArtifact.abi, erc20PredicateAddress)
}
burnERC20Tokens(token: address, amount: BN | string, options?: SendOptions) {
return this.web3Client.sendOnMatic(
- this._getERC20TokenContract(token).methods.withdraw(amount),
+ this._getERC20TokenContract(token).methods.withdraw(this.encode(amount)),
options
)
}
- startExitWithBurntERC20Tokens(burnERC20TokensReceipt) {
+ async startExitWithBurntERC20Tokens(burnERC20TxHash, options?) {
// check checkpoint
+ const lastChildBlock = await this.rootChain.getLastChildBlock()
+ const burnTx = await this.web3Client.getMaticWeb3().eth.getTransaction(burnERC20TxHash)
+ const receipt = await this.web3Client.getMaticWeb3().eth.getTransactionReceipt(burnERC20TxHash)
+ const block: any = await this.web3Client.getMaticWeb3().eth.getBlock(burnTx.blockNumber, true /* returnTransactionObjects */)
+
+ console.log('burnTx.blockNumber', burnTx.blockNumber, 'lastChildBlock', lastChildBlock)
+ assert.ok(new BN(lastChildBlock).gt(new BN(burnTx.blockNumber)), 'Burn transaction has not been checkpointed as yet')
+ const headerBlockNumber = await this.rootChain.findHeaderBlockNumber(burnTx.blockNumber)
+ const headerBlock = await this.web3Client.call(
+ this.rootChain.getRawContract().methods.headerBlocks(this.encode(headerBlockNumber))
+ )
+ console.log('headerBlockNumber', headerBlockNumber.toString(), 'headerBlock', headerBlock)
+ // build block proof
+ // const blockProof = '0x03567ec884f64162fb800d96e0a3bcddb507e63fe85fc0af2a804ecea05add499de833ba9f5661d8bcf20557572be8377fad0075c6b8f80893bf7d51604285e2023b1410778af7f76e117e62f2b221e23ee47f06fca092d841798f727f455e548e6b53def3660e5fb84c3a53ac72676597b246f67dc5605f87bafbb931e06fec95cc86cfca0a3459bd01b231d76e06c65176a13c9e3b9280d4a625057edd593852e2ff5a317004fc9df02d09f9348242a7546017d7d8cd971bed26ce4913230d38b0e5a5eb73fa2cabab1d77bfaec04629443cd1932b78f80c63def86624699cdf1e9df99ebfb565ac7dfa76c35624ca1f917206d5ccc0be9a754c398e1af4cc'
+ const blockProof = await Proofs.buildBlockProof(
+ this.web3Client.getMaticWeb3(),
+ // '218278',
+ // '218279',
+ headerBlock.start,
+ headerBlock.end,
+ burnTx.blockNumber
+ )
+ console.log('blockProof', blockProof)
+ // const txProof = (await Proofs.getTxProof(burnTx, block)).txParentNodes
+ const receiptProof = await Proofs.getReceiptProof(receipt, block, this.web3Client.getMaticWeb3())
+ const payload = this.buildPayload(
+ headerBlockNumber,
+ blockProof,
+ burnTx.blockNumber,
+ block.timestamp,
+ Buffer.from(block.transactionsRoot.slice(2), 'hex'),
+ Buffer.from(block.receiptsRoot.slice(2), 'hex'),
+ Proofs.getReceiptBytes(receipt), // rlp encoded
+ receiptProof.parentNodes,
+ receiptProof.path,
+ 1 // logIndex
+ )
+ console.log('payload', payload)
+ return this.web3Client.send(
+ this.erc20Predicate.methods.startExitWithBurntTokens(payload),
+ options
+ )
+ }
+
+ buildPayload(
+ headerNumber, buildBlockProof, blockNumber, timestamp, transactionsRoot, receiptsRoot, receipt, receiptParentNodes, path, logIndex) {
+ return ethUtils.bufferToHex(ethUtils.rlp.encode([
+ headerNumber,
+ buildBlockProof,
+ blockNumber,
+ timestamp,
+ ethUtils.bufferToHex(transactionsRoot),
+ ethUtils.bufferToHex(receiptsRoot),
+ ethUtils.bufferToHex(receipt),
+ ethUtils.bufferToHex(ethUtils.rlp.encode(receiptParentNodes)),
+ ethUtils.bufferToHex(ethUtils.rlp.encode(path)),
+ logIndex
+ ]))
}
private _getERC20TokenContract(token: address) {

Add Registry, startExitWithBurntERC20Tokens