@nomad-xyz/sdk-bridge
Version:
253 lines • 9.56 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.TransferMessage = exports.BridgeMessage = exports.parseBody = void 0;
const bignumber_1 = require("@ethersproject/bignumber");
const bytes_1 = require("@ethersproject/bytes");
const ethers_1 = require("ethers");
const sdk_1 = require("@nomad-xyz/sdk");
const ACTION_LEN = {
identifier: 1,
tokenId: 36,
transfer: 97,
};
function parseAction(buf) {
// Transfer
if (buf.length === ACTION_LEN.transfer) {
// trim identifer
const actionType = buf[0];
buf = buf.slice(ACTION_LEN.identifier);
return {
type: 'transfer',
to: (0, bytes_1.hexlify)(buf.slice(0, 32)),
amount: bignumber_1.BigNumber.from((0, bytes_1.hexlify)(buf.slice(32, 64))),
detailsHash: (0, bytes_1.hexlify)(buf.slice(64)),
allowFast: actionType === 4,
};
}
throw new Error('Bad action');
}
function parseBody(messageBody) {
const buf = (0, bytes_1.arrayify)(messageBody);
const tokenId = buf.slice(0, 36);
const token = {
domain: Buffer.from(tokenId).readUInt32BE(0),
id: (0, bytes_1.hexlify)(tokenId.slice(4, 36)),
};
const action = parseAction(buf.slice(36));
const parsedMessage = {
action,
token,
};
return parsedMessage;
}
exports.parseBody = parseBody;
/**
* The BridgeMessage extends {@link nomadMessage} with Bridge-specific
* functionality.
*/
class BridgeMessage extends sdk_1.NomadMessage {
/**
* @hideconstructor
*/
constructor(context, dispatch, token, callerKnowsWhatTheyAreDoing, _backend) {
if (!callerKnowsWhatTheyAreDoing) {
throw new Error('Use `fromReceipt` to instantiate');
}
super(context, dispatch, _backend);
const fromBridge = context.mustGetBridge(this.message.from);
const toBridge = context.mustGetBridge(this.message.destination);
this.fromBridge = fromBridge;
this.toBridge = toBridge;
this.token = token;
}
get backend() {
const backend = this._backend || this.context._backend;
if (!backend) {
throw new Error(`No backend in the context`);
}
return backend;
}
async getReceived() {
return await this.backend.receivedTx(this.messageHash);
}
async getSender() {
return await this.backend.sender(this.messageHash);
}
static async bridgeFirstFromBackend(context, transactionHash) {
const m = await this.baseFirstFromBackend(context, transactionHash);
const bm = BridgeMessage.fromNomadMessage(context, m, context._backend);
return bm;
}
static async bridgeFromMessageHash(context, messageHash) {
const m = await this.baseFromMessageHash(context, messageHash);
const bm = BridgeMessage.fromNomadMessage(context, m, context._backend);
return bm;
}
/**
* Attempt to instantiate a BridgeMessage from an existing
* {@link nomadMessage}
*
* @param context The {@link NomadContext} to use.
* @param nomadMessage The existing nomadMessage
* @returns A Bridge message
* @throws if the message cannot be parsed as a bridge message
*/
static fromNomadMessage(context, nomadMessage, _backend) {
const parsedMessageBody = parseBody(nomadMessage.message.body);
return new TransferMessage(context, nomadMessage.dispatch, parsedMessageBody, _backend || context._backend);
}
/**
* Attempt to instantiate some BridgeMessages from a transaction receipt
*
* @param context The {@link NomadContext} to use.
* @param receipt The receipt
* @returns an array of {@link BridgeMessage} objects
* @throws if any message cannot be parsed as a bridge message
*/
static async fromReceipt(context, receipt, _backend) {
const nomadMessages = await sdk_1.NomadMessage.baseFromReceipt(context, receipt);
const bridgeMessages = [];
for (const nomadMessage of nomadMessages) {
try {
const bridgeMessage = BridgeMessage.fromNomadMessage(context, nomadMessage, _backend);
bridgeMessages.push(bridgeMessage);
}
catch (e) {
// catch error if nomadMessage isn't a BridgeMessage
}
}
return bridgeMessages;
}
/**
* Attempt to instantiate EXACTLY one BridgeMessage from a transaction receipt
*
* @param context The {@link BridgeContext} to use.
* @param receipt The receipt
* @returns an array of {@link BridgeMessage} objects
* @throws if any message cannot be parsed as a bridge message, or if there
* is not EXACTLY 1 BridgeMessage in the receipt
*/
static async singleFromReceipt(context, receipt, _backend) {
const messages = await BridgeMessage.fromReceipt(context, receipt, _backend);
if (messages.length !== 1) {
throw new Error('Expected single Dispatch in transaction');
}
return messages[0];
}
/**
* Attempt to instantiate some BridgeMessages from a transaction hash by
* retrieving and parsing the receipt.
*
* @param context The {@link NomadContext} to use.
* @param nameOrDomain the domain on which the receipt was logged
* @param transactionHash the transaction hash on the origin chain
* @returns an array of {@link BridgeMessage} objects
* @throws if any message cannot be parsed as a bridge message
*/
static async fromTransactionHash(context, nameOrDomain, transactionHash, _backend) {
const provider = context.mustGetProvider(nameOrDomain);
const receipt = await provider.getTransactionReceipt(transactionHash);
if (!receipt) {
throw new Error(`No receipt for ${transactionHash} on ${nameOrDomain}`);
}
return BridgeMessage.fromReceipt(context, receipt, _backend);
}
/**
* Attempt to instantiate EXACTLY one BridgeMessages from a transaction hash
* by retrieving and parsing the receipt.
*
* @param context The {@link NomadContext} to use.
* @param nameOrDomain the domain on which the receipt was logged
* @param transactionHash the transaction hash on the origin chain
* @returns an array of {@link BridgeMessage} objects
* @throws if any message cannot be parsed as a bridge message, or if there is
* not EXACTLY one such message
*/
static async singleFromTransactionHash(context, nameOrDomain, transactionHash, _backend) {
const provider = context.mustGetProvider(nameOrDomain);
const receipt = await provider.getTransactionReceipt(transactionHash);
if (!receipt) {
throw new Error(`No receipt for ${transactionHash} on ${nameOrDomain}`);
}
return BridgeMessage.singleFromReceipt(context, receipt, _backend);
}
/**
* Resolves the asset that is being transfered
*
* WARNING: do not hold references to these contract, as they will not be
* reconnected in the event the chain connection changes.
*
* @returns The resolved token information.
*/
async asset() {
return await this.context.resolveRepresentations(this.token);
}
/**
* Resolves an interface for the asset that is being transfered on the chain
* FROM WHICH it is being transferred
*
* WARNING: do not hold references to this contract, as it will not be
* reconnected in the event the chain connection changes.
*
* @returns The resolved token interface.
*/
async assetAtOrigin() {
return (await this.asset()).tokens.get(this.origin);
}
/**
* Resolves an interface for the asset that is being transfered on the chain
* TO WHICH it is being transferred
*
* WARNING: do not hold references to this contract, as it will not be
* reconnected in the event the chain connection changes.
*
* @returns The resolved token interface.
*/
async assetAtDestination() {
return (await this.asset()).tokens.get(this.destination);
}
}
exports.BridgeMessage = BridgeMessage;
/**
* A TransferMessage extends the {@link BridgeMessage} with transfer-specific
* functionality.
*/
class TransferMessage extends BridgeMessage {
constructor(context, dispatch, parsed, _backend) {
super(context, dispatch, parsed.token, true, _backend);
this.action = parsed.action;
}
/**
* Check if the transfer has been prefilled using the fast liquidity system.
*
* @returns true if the transfer has been prefilled. Else false.
*/
async currentlyPrefilled() {
const bridge = this.context.mustGetBridge(this.destination);
const lpAddress = await bridge.bridgeRouter.liquidityProvider(this.prefillId);
if (lpAddress !== ethers_1.ethers.constants.AddressZero) {
return true;
}
return false;
}
/**
* The amount of tokens being transferred (in the smallest unit)
*/
get amount() {
return this.action.amount;
}
/**
* The identifier for the recipient of the tokens
*/
get to() {
return this.action.to;
}
/**
* The ID used for prefilling this transfer message.
*/
get prefillId() {
return this.bodyHash;
}
}
exports.TransferMessage = TransferMessage;
//# sourceMappingURL=BridgeMessage.js.map