@arbitrum/sdk
Version:
Typescript library client-side interactions with Arbitrum
544 lines (543 loc) • 28.9 kB
JavaScript
/*
* Copyright 2021, Offchain Labs, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-env node */
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.EthDepositMessage = exports.ParentToChildMessageWriter = exports.ParentToChildMessageReaderClassic = exports.ParentToChildMessageReader = exports.ParentToChildMessage = exports.EthDepositMessageStatus = exports.ParentToChildMessageStatus = void 0;
const bignumber_1 = require("@ethersproject/bignumber");
const bytes_1 = require("@ethersproject/bytes");
const address_1 = require("@ethersproject/address");
const keccak256_1 = require("@ethersproject/keccak256");
const ArbRetryableTx__factory_1 = require("../abi/factories/ArbRetryableTx__factory");
const constants_1 = require("../dataEntities/constants");
const signerOrProvider_1 = require("../dataEntities/signerOrProvider");
const errors_1 = require("../dataEntities/errors");
const ethers_1 = require("ethers");
const ChildTransaction_1 = require("./ChildTransaction");
const lib_1 = require("../utils/lib");
const eventFetcher_1 = require("../utils/eventFetcher");
const logger_1 = require("@ethersproject/logger");
const networks_1 = require("../dataEntities/networks");
var ParentToChildMessageStatus;
(function (ParentToChildMessageStatus) {
/**
* The retryable ticket has yet to be created
*/
ParentToChildMessageStatus[ParentToChildMessageStatus["NOT_YET_CREATED"] = 1] = "NOT_YET_CREATED";
/**
* An attempt was made to create the retryable ticket, but it failed.
* This could be due to not enough submission cost being paid by the Parent transaction
*/
ParentToChildMessageStatus[ParentToChildMessageStatus["CREATION_FAILED"] = 2] = "CREATION_FAILED";
/**
* The retryable ticket has been created but has not been redeemed. This could be due to the
* auto redeem failing, or if the params (max chain gas price) * (max chain gas) = 0 then no auto
* redeem tx is ever issued. An auto redeem is also never issued for ETH deposits.
* A manual redeem is now required.
*/
ParentToChildMessageStatus[ParentToChildMessageStatus["FUNDS_DEPOSITED_ON_CHILD"] = 3] = "FUNDS_DEPOSITED_ON_CHILD";
/**
* The retryable ticket has been redeemed (either by auto, or manually) and the
* chain transaction has been executed
*/
ParentToChildMessageStatus[ParentToChildMessageStatus["REDEEMED"] = 4] = "REDEEMED";
/**
* The message has either expired or has been canceled. It can no longer be redeemed.
*/
ParentToChildMessageStatus[ParentToChildMessageStatus["EXPIRED"] = 5] = "EXPIRED";
})(ParentToChildMessageStatus || (exports.ParentToChildMessageStatus = ParentToChildMessageStatus = {}));
var EthDepositMessageStatus;
(function (EthDepositMessageStatus) {
/**
* ETH is not deposited on Chain yet
*/
EthDepositMessageStatus[EthDepositMessageStatus["PENDING"] = 1] = "PENDING";
/**
* ETH is deposited successfully on Chain
*/
EthDepositMessageStatus[EthDepositMessageStatus["DEPOSITED"] = 2] = "DEPOSITED";
})(EthDepositMessageStatus || (exports.EthDepositMessageStatus = EthDepositMessageStatus = {}));
class ParentToChildMessage {
/**
* The submit retryable transactions use the typed transaction envelope 2718.
* The id of these transactions is the hash of the RLP encoded transaction.
* @param childChainId
* @param fromAddress the aliased address that called the Parent inbox as emitted in the bridge event.
* @param messageNumber
* @param parentBaseFee
* @param destAddress
* @param childCallValue
* @param parentCallValue
* @param maxSubmissionFee
* @param excessFeeRefundAddress refund address specified in the retryable creation. Note the Parent inbox aliases this address if it is a Parent smart contract. The user is expected to provide this value already aliased when needed.
* @param callValueRefundAddress refund address specified in the retryable creation. Note the Parent inbox aliases this address if it is a Parent smart contract. The user is expected to provide this value already aliased when needed.
* @param gasLimit
* @param maxFeePerGas
* @param data
* @returns
*/
static calculateSubmitRetryableId(childChainId, fromAddress, messageNumber, parentBaseFee, destAddress, childCallValue, parentCallValue, maxSubmissionFee, excessFeeRefundAddress, callValueRefundAddress, gasLimit, maxFeePerGas, data) {
const formatNumber = (value) => {
return ethers_1.ethers.utils.stripZeros(value.toHexString());
};
const chainId = bignumber_1.BigNumber.from(childChainId);
const msgNum = bignumber_1.BigNumber.from(messageNumber);
const fields = [
formatNumber(chainId),
(0, bytes_1.zeroPad)(formatNumber(msgNum), 32),
fromAddress,
formatNumber(parentBaseFee),
formatNumber(parentCallValue),
formatNumber(maxFeePerGas),
formatNumber(gasLimit),
// when destAddress is 0x0, arbos treat that as nil
destAddress === ethers_1.ethers.constants.AddressZero ? '0x' : destAddress,
formatNumber(childCallValue),
callValueRefundAddress,
formatNumber(maxSubmissionFee),
excessFeeRefundAddress,
data,
];
// arbitrum submit retry transactions have type 0x69
const rlpEnc = ethers_1.ethers.utils.hexConcat([
'0x69',
ethers_1.ethers.utils.RLP.encode(fields),
]);
return ethers_1.ethers.utils.keccak256(rlpEnc);
}
static fromEventComponents(chainSignerOrProvider, chainId, sender, messageNumber, parentBaseFee, messageData) {
return signerOrProvider_1.SignerProviderUtils.isSigner(chainSignerOrProvider)
? new ParentToChildMessageWriter(chainSignerOrProvider, chainId, sender, messageNumber, parentBaseFee, messageData)
: new ParentToChildMessageReader(chainSignerOrProvider, chainId, sender, messageNumber, parentBaseFee, messageData);
}
constructor(chainId, sender, messageNumber, parentBaseFee, messageData) {
this.chainId = chainId;
this.sender = sender;
this.messageNumber = messageNumber;
this.parentBaseFee = parentBaseFee;
this.messageData = messageData;
this.retryableCreationId = ParentToChildMessage.calculateSubmitRetryableId(chainId, sender, messageNumber, parentBaseFee, messageData.destAddress, messageData.l2CallValue, messageData.l1Value, messageData.maxSubmissionFee, messageData.excessFeeRefundAddress, messageData.callValueRefundAddress, messageData.gasLimit, messageData.maxFeePerGas, messageData.data);
}
}
exports.ParentToChildMessage = ParentToChildMessage;
class ParentToChildMessageReader extends ParentToChildMessage {
constructor(childProvider, chainId, sender, messageNumber, parentBaseFee, messageData) {
super(chainId, sender, messageNumber, parentBaseFee, messageData);
this.childProvider = childProvider;
}
/**
* Try to get the receipt for the retryable ticket creation.
* This is the Chain transaction that creates the retryable ticket.
* If confirmations or timeout is provided, this will wait for the ticket to be created
* @returns Null if retryable has not been created
*/
async getRetryableCreationReceipt(confirmations, timeout) {
if (!this.retryableCreationReceipt) {
this.retryableCreationReceipt = await (0, lib_1.getTransactionReceipt)(this.childProvider, this.retryableCreationId, confirmations, timeout);
}
return this.retryableCreationReceipt || null;
}
/**
* When retryable tickets are created, and gas is supplied to it, an attempt is
* made to redeem the ticket straight away. This is called an auto redeem.
* @returns TransactionReceipt of the auto redeem attempt if exists, otherwise null
*/
async getAutoRedeemAttempt() {
const creationReceipt = await this.getRetryableCreationReceipt();
if (creationReceipt) {
const chainReceipt = new ChildTransaction_1.ChildTransactionReceipt(creationReceipt);
const redeemEvents = chainReceipt.getRedeemScheduledEvents();
if (redeemEvents.length === 1) {
return await this.childProvider.getTransactionReceipt(redeemEvents[0].retryTxHash);
}
else if (redeemEvents.length > 1) {
throw new errors_1.ArbSdkError(`Unexpected number of redeem events for retryable creation tx. ${creationReceipt} ${redeemEvents}`);
}
}
return null;
}
/**
* Receipt for the successful chain transaction created by this message.
* @returns TransactionReceipt of the first successful redeem if exists, otherwise the current status of the message.
*/
async getSuccessfulRedeem() {
var _a;
const chainNetwork = await (0, networks_1.getArbitrumNetwork)(this.childProvider);
const eventFetcher = new eventFetcher_1.EventFetcher(this.childProvider);
const creationReceipt = await this.getRetryableCreationReceipt();
if (!(0, lib_1.isDefined)(creationReceipt)) {
// retryable was never created, or not created yet
// therefore it cant have been redeemed or be expired
return { status: ParentToChildMessageStatus.NOT_YET_CREATED };
}
if (creationReceipt.status === 0) {
return { status: ParentToChildMessageStatus.CREATION_FAILED };
}
// check the auto redeem first to avoid doing costly log queries in the happy case
const autoRedeem = await this.getAutoRedeemAttempt();
if (autoRedeem && autoRedeem.status === 1) {
return {
childTxReceipt: autoRedeem,
status: ParentToChildMessageStatus.REDEEMED,
};
}
if (await this.retryableExists()) {
// the retryable was created and still exists
// therefore it cant have been redeemed or be expired
return {
status: ParentToChildMessageStatus.FUNDS_DEPOSITED_ON_CHILD,
};
}
// from this point on we know that the retryable was created but does not exist,
// so the retryable was either successfully redeemed, or it expired
// the auto redeem didnt exist or wasnt successful, look for a later manual redeem
// to do this we need to filter through the whole lifetime of the ticket looking
// for relevant redeem scheduled events
let increment = 1000;
let fromBlock = await this.childProvider.getBlock(creationReceipt.blockNumber);
let timeout = fromBlock.timestamp +
((_a = chainNetwork.retryableLifetimeSeconds) !== null && _a !== void 0 ? _a : constants_1.SEVEN_DAYS_IN_SECONDS);
const queriedRange = [];
const maxBlock = await this.childProvider.getBlockNumber();
while (fromBlock.number < maxBlock) {
const toBlockNumber = Math.min(fromBlock.number + increment, maxBlock);
// using fromBlock.number would lead to 1 block overlap
// not fixing it here to keep the code simple
const outerBlockRange = { from: fromBlock.number, to: toBlockNumber };
queriedRange.push(outerBlockRange);
const redeemEvents = await eventFetcher.getEvents(ArbRetryableTx__factory_1.ArbRetryableTx__factory, contract => contract.filters.RedeemScheduled(this.retryableCreationId), {
fromBlock: outerBlockRange.from,
toBlock: outerBlockRange.to,
address: constants_1.ARB_RETRYABLE_TX_ADDRESS,
});
const successfulRedeem = (await Promise.all(redeemEvents.map(e => this.childProvider.getTransactionReceipt(e.event.retryTxHash)))).filter(r => (0, lib_1.isDefined)(r) && r.status === 1);
if (successfulRedeem.length > 1)
throw new errors_1.ArbSdkError(`Unexpected number of successful redeems. Expected only one redeem for ticket ${this.retryableCreationId}, but found ${successfulRedeem.length}.`);
if (successfulRedeem.length == 1)
return {
childTxReceipt: successfulRedeem[0],
status: ParentToChildMessageStatus.REDEEMED,
};
const toBlock = await this.childProvider.getBlock(toBlockNumber);
if (toBlock.timestamp > timeout) {
// Check for LifetimeExtended event
while (queriedRange.length > 0) {
const blockRange = queriedRange.shift();
const keepaliveEvents = await eventFetcher.getEvents(ArbRetryableTx__factory_1.ArbRetryableTx__factory, contract => contract.filters.LifetimeExtended(this.retryableCreationId), {
fromBlock: blockRange.from,
toBlock: blockRange.to,
address: constants_1.ARB_RETRYABLE_TX_ADDRESS,
});
if (keepaliveEvents.length > 0) {
timeout = keepaliveEvents
.map(e => e.event.newTimeout.toNumber())
.sort()
.reverse()[0];
break;
}
}
// the retryable no longer exists, but we've searched beyond the timeout
// so it must have expired
if (toBlock.timestamp > timeout)
break;
// It is possible to have another keepalive in the last range as it might include block after previous timeout
while (queriedRange.length > 1)
queriedRange.shift();
}
const processedSeconds = toBlock.timestamp - fromBlock.timestamp;
if (processedSeconds != 0) {
// find the increment that cover ~ 1 day
increment = Math.ceil((increment * 86400) / processedSeconds);
}
fromBlock = toBlock;
}
// we know from earlier that the retryable no longer exists, so if we havent found the redemption
// we know that it must have expired
return { status: ParentToChildMessageStatus.EXPIRED };
}
/**
* Has this message expired. Once expired the retryable ticket can no longer be redeemed.
* @deprecated Will be removed in v3.0.0
* @returns
*/
async isExpired() {
return await this.retryableExists();
}
async retryableExists() {
const currentTimestamp = bignumber_1.BigNumber.from((await this.childProvider.getBlock('latest')).timestamp);
try {
const timeoutTimestamp = await this.getTimeout();
// timeoutTimestamp returns the timestamp at which the retryable ticket expires
// it can also return revert if the ticket chainTx does not exist
return currentTimestamp.lte(timeoutTimestamp);
}
catch (err) {
if (err instanceof Error &&
err.code ===
logger_1.Logger.errors.CALL_EXCEPTION &&
err.errorName === 'NoTicketWithID') {
return false;
}
throw err;
}
}
async status() {
return (await this.getSuccessfulRedeem()).status;
}
/**
* Wait for the retryable ticket to be created, for it to be redeemed, and for the chainTx to be executed.
* Note: The terminal status of a transaction that only does an eth deposit is FUNDS_DEPOSITED_ON_CHILD as
* no Chain transaction needs to be executed, however the terminal state of any other transaction is REDEEMED
* which represents that the retryable ticket has been redeemed and the Chain tx has been executed.
* @param confirmations Amount of confirmations the retryable ticket and the auto redeem receipt should have
* @param timeout Amount of time to wait for the retryable ticket to be created
* Defaults to 15 minutes, as by this time all transactions are expected to be included on Chain. Throws on timeout.
* @returns The wait result contains a status, and optionally the chainTxReceipt.
* If the status is "REDEEMED" then a chainTxReceipt is also available on the result.
* If the status has any other value then chainTxReceipt is not populated.
*/
async waitForStatus(confirmations, timeout) {
const chosenTimeout = (0, lib_1.isDefined)(timeout) ? timeout : constants_1.DEFAULT_DEPOSIT_TIMEOUT;
// try to wait for the retryable ticket to be created
const _retryableCreationReceipt = await this.getRetryableCreationReceipt(confirmations, chosenTimeout);
if (!_retryableCreationReceipt) {
if (confirmations || chosenTimeout) {
throw new errors_1.ArbSdkError(`Timed out waiting to retrieve retryable creation receipt: ${this.retryableCreationId}.`);
}
else {
throw new errors_1.ArbSdkError(`Retryable creation receipt not found ${this.retryableCreationId}.`);
}
}
return await this.getSuccessfulRedeem();
}
/**
* The minimium lifetime of a retryable tx
* @returns
*/
static async getLifetime(childProvider) {
const arbRetryableTx = ArbRetryableTx__factory_1.ArbRetryableTx__factory.connect(constants_1.ARB_RETRYABLE_TX_ADDRESS, childProvider);
return await arbRetryableTx.getLifetime();
}
/**
* Timestamp at which this message expires
* @returns
*/
async getTimeout() {
const arbRetryableTx = ArbRetryableTx__factory_1.ArbRetryableTx__factory.connect(constants_1.ARB_RETRYABLE_TX_ADDRESS, this.childProvider);
return await arbRetryableTx.getTimeout(this.retryableCreationId);
}
/**
* Address to which CallValue will be credited to on Chain if the retryable ticket times out or is cancelled.
* The Beneficiary is also the address with the right to cancel a Retryable Ticket (if the ticket hasn’t been redeemed yet).
* @returns
*/
getBeneficiary() {
const arbRetryableTx = ArbRetryableTx__factory_1.ArbRetryableTx__factory.connect(constants_1.ARB_RETRYABLE_TX_ADDRESS, this.childProvider);
return arbRetryableTx.getBeneficiary(this.retryableCreationId);
}
}
exports.ParentToChildMessageReader = ParentToChildMessageReader;
class ParentToChildMessageReaderClassic {
constructor(childProvider, chainId, messageNumber) {
const bitFlip = (num) => num.or(bignumber_1.BigNumber.from(1).shl(255));
this.messageNumber = messageNumber;
this.childProvider = childProvider;
this.retryableCreationId = (0, keccak256_1.keccak256)((0, bytes_1.concat)([
(0, bytes_1.zeroPad)(bignumber_1.BigNumber.from(chainId).toHexString(), 32),
(0, bytes_1.zeroPad)(bitFlip(this.messageNumber).toHexString(), 32),
]));
this.autoRedeemId = (0, keccak256_1.keccak256)((0, bytes_1.concat)([
(0, bytes_1.zeroPad)(this.retryableCreationId, 32),
(0, bytes_1.zeroPad)(bignumber_1.BigNumber.from(1).toHexString(), 32),
]));
this.childTxHash = (0, keccak256_1.keccak256)((0, bytes_1.concat)([
(0, bytes_1.zeroPad)(this.retryableCreationId, 32),
(0, bytes_1.zeroPad)(bignumber_1.BigNumber.from(0).toHexString(), 32),
]));
}
calculateChainDerivedHash(retryableCreationId) {
return (0, keccak256_1.keccak256)((0, bytes_1.concat)([
(0, bytes_1.zeroPad)(retryableCreationId, 32),
// BN 0 meaning Chain TX
(0, bytes_1.zeroPad)(bignumber_1.BigNumber.from(0).toHexString(), 32),
]));
}
/**
* Try to get the receipt for the retryable ticket creation.
* This is the Chain transaction that creates the retryable ticket.
* If confirmations or timeout is provided, this will wait for the ticket to be created
* @returns Null if retryable has not been created
*/
async getRetryableCreationReceipt(confirmations, timeout) {
if (!this.retryableCreationReceipt) {
this.retryableCreationReceipt = await (0, lib_1.getTransactionReceipt)(this.childProvider, this.retryableCreationId, confirmations, timeout);
}
return this.retryableCreationReceipt || null;
}
async status() {
const creationReceipt = await this.getRetryableCreationReceipt();
if (!(0, lib_1.isDefined)(creationReceipt)) {
return ParentToChildMessageStatus.NOT_YET_CREATED;
}
if (creationReceipt.status === 0) {
return ParentToChildMessageStatus.CREATION_FAILED;
}
const chainDerivedHash = this.calculateChainDerivedHash(this.retryableCreationId);
const chainTxReceipt = await this.childProvider.getTransactionReceipt(chainDerivedHash);
if (chainTxReceipt && chainTxReceipt.status === 1) {
return ParentToChildMessageStatus.REDEEMED;
}
return ParentToChildMessageStatus.EXPIRED;
}
}
exports.ParentToChildMessageReaderClassic = ParentToChildMessageReaderClassic;
class ParentToChildMessageWriter extends ParentToChildMessageReader {
constructor(chainSigner, chainId, sender, messageNumber, parentBaseFee, messageData) {
super(chainSigner.provider, chainId, sender, messageNumber, parentBaseFee, messageData);
this.chainSigner = chainSigner;
if (!chainSigner.provider)
throw new errors_1.ArbSdkError('Signer not connected to provider.');
}
/**
* Manually redeem the retryable ticket.
* Throws if message status is not ParentToChildMessageStatus.FUNDS_DEPOSITED_ON_CHILD
*/
async redeem(overrides) {
const status = await this.status();
if (status === ParentToChildMessageStatus.FUNDS_DEPOSITED_ON_CHILD) {
const arbRetryableTx = ArbRetryableTx__factory_1.ArbRetryableTx__factory.connect(constants_1.ARB_RETRYABLE_TX_ADDRESS, this.chainSigner);
const redeemTx = await arbRetryableTx.redeem(this.retryableCreationId, Object.assign({}, overrides));
return ChildTransaction_1.ChildTransactionReceipt.toRedeemTransaction(ChildTransaction_1.ChildTransactionReceipt.monkeyPatchWait(redeemTx), this.childProvider);
}
else {
throw new errors_1.ArbSdkError(`Cannot redeem as retryable does not exist. Message status: ${ParentToChildMessageStatus[status]} must be: ${ParentToChildMessageStatus[ParentToChildMessageStatus.FUNDS_DEPOSITED_ON_CHILD]}.`);
}
}
/**
* Cancel the retryable ticket.
* Throws if message status is not ParentToChildMessageStatus.FUNDS_DEPOSITED_ON_CHILD
*/
async cancel(overrides) {
const status = await this.status();
if (status === ParentToChildMessageStatus.FUNDS_DEPOSITED_ON_CHILD) {
const arbRetryableTx = ArbRetryableTx__factory_1.ArbRetryableTx__factory.connect(constants_1.ARB_RETRYABLE_TX_ADDRESS, this.chainSigner);
return await arbRetryableTx.cancel(this.retryableCreationId, overrides);
}
else {
throw new errors_1.ArbSdkError(`Cannot cancel as retryable does not exist. Message status: ${ParentToChildMessageStatus[status]} must be: ${ParentToChildMessageStatus[ParentToChildMessageStatus.FUNDS_DEPOSITED_ON_CHILD]}.`);
}
}
/**
* Increase the timeout of a retryable ticket.
* Throws if message status is not ParentToChildMessageStatus.FUNDS_DEPOSITED_ON_CHILD
*/
async keepAlive(overrides) {
const status = await this.status();
if (status === ParentToChildMessageStatus.FUNDS_DEPOSITED_ON_CHILD) {
const arbRetryableTx = ArbRetryableTx__factory_1.ArbRetryableTx__factory.connect(constants_1.ARB_RETRYABLE_TX_ADDRESS, this.chainSigner);
return await arbRetryableTx.keepalive(this.retryableCreationId, overrides);
}
else {
throw new errors_1.ArbSdkError(`Cannot keep alive as retryable does not exist. Message status: ${ParentToChildMessageStatus[status]} must be: ${ParentToChildMessageStatus[ParentToChildMessageStatus.FUNDS_DEPOSITED_ON_CHILD]}.`);
}
}
}
exports.ParentToChildMessageWriter = ParentToChildMessageWriter;
/**
* A message for Eth deposits from Parent to Child
*/
class EthDepositMessage {
static calculateDepositTxId(childChainId, messageNumber, fromAddress, toAddress, value) {
const formatNumber = (numberVal) => {
return ethers_1.ethers.utils.stripZeros(numberVal.toHexString());
};
const chainId = bignumber_1.BigNumber.from(childChainId);
const msgNum = bignumber_1.BigNumber.from(messageNumber);
// https://github.com/OffchainLabs/go-ethereum/blob/07e017aa73e32be92aadb52fa327c552e1b7b118/core/types/arb_types.go#L302-L308
const fields = [
formatNumber(chainId),
(0, bytes_1.zeroPad)(formatNumber(msgNum), 32),
(0, address_1.getAddress)(fromAddress),
(0, address_1.getAddress)(toAddress),
formatNumber(value),
];
// arbitrum eth deposit transactions have type 0x64
const rlpEnc = ethers_1.ethers.utils.hexConcat([
'0x64',
ethers_1.ethers.utils.RLP.encode(fields),
]);
return ethers_1.ethers.utils.keccak256(rlpEnc);
}
/**
* Parse the data field in
* event InboxMessageDelivered(uint256 indexed messageNum, bytes data);
* @param eventData
* @returns destination and amount
*/
static parseEthDepositData(eventData) {
// https://github.com/OffchainLabs/nitro/blob/aa84e899cbc902bf6da753b1d66668a1def2c106/contracts/src/bridge/Inbox.sol#Chain42
// ethers.defaultAbiCoder doesnt decode packed args, so we do a hardcoded parsing
const addressEnd = 2 + 20 * 2;
const to = (0, address_1.getAddress)('0x' + eventData.substring(2, addressEnd));
const value = bignumber_1.BigNumber.from('0x' + eventData.substring(addressEnd));
return { to, value };
}
/**
* Create an EthDepositMessage from data emitted in event when calling ethDeposit on Inbox.sol
* @param childProvider
* @param messageNumber The message number in the Inbox.InboxMessageDelivered event
* @param senderAddr The sender address from Bridge.MessageDelivered event
* @param inboxMessageEventData The data field from the Inbox.InboxMessageDelivered event
* @returns
*/
static async fromEventComponents(childProvider, messageNumber, senderAddr, inboxMessageEventData) {
const chainId = (await childProvider.getNetwork()).chainId;
const { to, value } = EthDepositMessage.parseEthDepositData(inboxMessageEventData);
return new EthDepositMessage(childProvider, chainId, messageNumber, senderAddr, to, value);
}
/**
*
* @param childProvider
* @param childChainId
* @param messageNumber
* @param to Recipient address of the ETH on Chain
* @param value
*/
constructor(childProvider, childChainId, messageNumber, from, to, value) {
this.childProvider = childProvider;
this.childChainId = childChainId;
this.messageNumber = messageNumber;
this.from = from;
this.to = to;
this.value = value;
this.childTxHash = EthDepositMessage.calculateDepositTxId(childChainId, messageNumber, from, to, value);
}
async status() {
const receipt = await this.childProvider.getTransactionReceipt(this.childTxHash);
if (receipt === null)
return EthDepositMessageStatus.PENDING;
else
return EthDepositMessageStatus.DEPOSITED;
}
async wait(confirmations, timeout) {
const chosenTimeout = (0, lib_1.isDefined)(timeout) ? timeout : constants_1.DEFAULT_DEPOSIT_TIMEOUT;
if (!this.childTxReceipt) {
this.childTxReceipt = await (0, lib_1.getTransactionReceipt)(this.childProvider, this.childTxHash, confirmations, chosenTimeout);
}
return this.childTxReceipt || null;
}
}
exports.EthDepositMessage = EthDepositMessage;