Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions modules/sdk-coin-flrp/src/lib/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,9 +254,96 @@ export class Transaction extends BaseTransaction {
signatures: this.signature,
outputs: this.outputs,
changeOutputs: this.changeOutputs,
sourceChain: this.sourceChain,
destinationChain: this.destinationChain,
};
}

/**
* Get the source chain id or undefined if it's not a cross chain transfer.
*/
get sourceChain(): string | undefined {
const tx = (this._flareTransaction as UnsignedTx).getTx();

switch (this.type) {
case TransactionType.Import:
if (this.isTransactionForCChain) {
// C-chain Import: source is the chain we're importing FROM (P-chain)
const importTx = tx as evmSerial.ImportTx;
return this.blockchainIDtoAlias(Buffer.from(importTx.sourceChain.toBytes()));
} else {
// P-chain Import: source is the chain we're importing FROM (C-chain)
const pvmImportTx = tx as pvmSerial.ImportTx;
return this.blockchainIDtoAlias(Buffer.from(pvmImportTx.sourceChain.toBytes()));
}

case TransactionType.Export:
if (this.isTransactionForCChain) {
// C-chain Export: source is C-chain (the blockchain ID)
const exportTx = tx as evmSerial.ExportTx;
return this.blockchainIDtoAlias(Buffer.from(exportTx.blockchainId.toBytes()));
} else {
// P-chain Export: source is P-chain (the blockchain ID from baseTx)
const pvmExportTx = tx as pvmSerial.ExportTx;
return this.blockchainIDtoAlias(Buffer.from(pvmExportTx.baseTx.BlockchainId.toBytes()));
}

default:
return undefined;
}
}

/**
* Get the destination chain id or undefined if it's not a cross chain transfer.
*/
get destinationChain(): string | undefined {
const tx = (this._flareTransaction as UnsignedTx).getTx();

switch (this.type) {
case TransactionType.Import:
if (this.isTransactionForCChain) {
// C-chain Import: destination is C-chain (the blockchain ID)
const importTx = tx as evmSerial.ImportTx;
return this.blockchainIDtoAlias(Buffer.from(importTx.blockchainId.toBytes()));
} else {
// P-chain Import: destination is P-chain (the blockchain ID from baseTx)
const pvmImportTx = tx as pvmSerial.ImportTx;
return this.blockchainIDtoAlias(Buffer.from(pvmImportTx.baseTx.BlockchainId.toBytes()));
}

case TransactionType.Export:
if (this.isTransactionForCChain) {
// C-chain Export: destination is P-chain (the destination chain)
const exportTx = tx as evmSerial.ExportTx;
return this.blockchainIDtoAlias(Buffer.from(exportTx.destinationChain.toBytes()));
} else {
// P-chain Export: destination is C-chain (the destination chain)
const pvmExportTx = tx as pvmSerial.ExportTx;
return this.blockchainIDtoAlias(Buffer.from(pvmExportTx.destination.toBytes()));
}

default:
return undefined;
}
}

/**
* Convert a blockchainId buffer to string and return P or C alias if it matches any of those chains.
* @param {Buffer} blockchainIDBuffer
* @return {string} blockchainID or alias if exists.
* @private
*/
private blockchainIDtoAlias(blockchainIDBuffer: Buffer): string {
const blockchainId = utils.cb58Encode(blockchainIDBuffer);
if (blockchainId === this._network.cChainBlockchainID) {
return 'C';
}
if (blockchainId === this._network.blockchainID) {
return 'P';
}
return blockchainId;
}

setTransaction(tx: Tx): void {
this._flareTransaction = tx as UnsignedTx;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import 'should';
import { coins } from '@bitgo/statics';
import { TransactionBuilderFactory, TxData } from '../../../src/lib';
import { EXPORT_IN_P } from '../../resources/transactionData/exportInP';
import { IMPORT_IN_P } from '../../resources/transactionData/importInP';
import { IMPORT_IN_C } from '../../resources/transactionData/importInC';
import { EXPORT_IN_C } from '../../resources/transactionData/exportInC';

describe('Flrp Transaction Builder Factory', () => {
const factory = new TransactionBuilderFactory(coins.get('tflrp'));

describe('Cross chain transfer has source and destination chains', () => {
// P-chain Export to C-chain: source is P, destination is C
const p2cExportTxs = [EXPORT_IN_P.unsignedHex, EXPORT_IN_P.halfSigntxHex, EXPORT_IN_P.fullSigntxHex];

// C-chain Import from P-chain: source is P, destination is C
const p2cImportTxs = [IMPORT_IN_C.unsignedHex, IMPORT_IN_C.halfSigntxHex, IMPORT_IN_C.fullSigntxHex];

// P-chain Import from C-chain: source is C, destination is P
const c2pImportTxs = [IMPORT_IN_P.unsignedHex, IMPORT_IN_P.halfSigntxHex, IMPORT_IN_P.fullSigntxHex];

// C-chain Export to P-chain: source is C, destination is P
const c2pExportTxs = [EXPORT_IN_C.unsignedHex, EXPORT_IN_C.signedHex];

async function toJson(txHex: string): Promise<TxData> {
const txBuilder = factory.from(txHex);
const tx = await txBuilder.build();
return tx.toJson();
}

describe('P to C chain transfers', () => {
it('Should have sourceChain P and destinationChain C for Export from P-chain', async () => {
for (const rawTx of p2cExportTxs) {
const txJson = await toJson(rawTx);
txJson.sourceChain!.should.equal('P');
txJson.destinationChain!.should.equal('C');
}
});

it('Should have sourceChain P and destinationChain C for Import to C-chain', async () => {
for (const rawTx of p2cImportTxs) {
const txJson = await toJson(rawTx);
txJson.sourceChain!.should.equal('P');
txJson.destinationChain!.should.equal('C');
}
});
});

describe('C to P chain transfers', () => {
it('Should have sourceChain C and destinationChain P for Import to P-chain', async () => {
for (const rawTx of c2pImportTxs) {
const txJson = await toJson(rawTx);
txJson.sourceChain!.should.equal('C');
txJson.destinationChain!.should.equal('P');
}
});

it('Should have sourceChain C and destinationChain P for Export from C-chain', async () => {
for (const rawTx of c2pExportTxs) {
const txJson = await toJson(rawTx);
txJson.sourceChain!.should.equal('C');
txJson.destinationChain!.should.equal('P');
}
});
});
});
});