From 33dcef5de16551b5909ceab42882a5382c6a6cab Mon Sep 17 00:00:00 2001 From: Paul Bellamy Date: Tue, 30 May 2023 17:51:56 +0100 Subject: [PATCH] Add Contract support for strkey-style contract IDs (#612) * new Contract('C123...abc') support * review feedback --- src/contract.js | 32 ++++++++++++++++++----- test/unit/contract_test.js | 52 +++++++++++++++++++++++++++----------- types/index.d.ts | 2 +- 3 files changed, 64 insertions(+), 22 deletions(-) diff --git a/src/contract.js b/src/contract.js index f691e3fb..7b320486 100644 --- a/src/contract.js +++ b/src/contract.js @@ -1,5 +1,6 @@ import { Operation } from './operation'; import xdr from './xdr'; +import { StrKey } from './strkey'; /** * Create a new Contract object. @@ -12,23 +13,42 @@ import xdr from './xdr'; * @constructor * * @param {string} contractId - ID of the contract (ex. + * `CA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAXE`, or as a + * 32-byte hex string * `000000000000000000000000000000000000000000000000000000000000000001`). */ // TODO: Support contract deployment, maybe? export class Contract { - // TODO: Figure out contract owner/id stuff here. How should we represent that? constructor(contractId) { - // TODO: Add methods based on the contractSpec (or do that elsewhere?) - this._id = Buffer.from(contractId, 'hex'); + try { + // First, try it as a strkey + this._id = StrKey.decodeContract(contractId); + } catch (_) { + // If that fails, try it as a hex string + // TODO: Add methods based on the contractSpec (or do that elsewhere?) + const b = Buffer.from(contractId, 'hex'); + if (b.length !== 32) { + throw new Error('Invalid contract ID'); + } + this._id = b; + } } /** - * Returns Stellar contract ID as a hex string, ex. + * Returns Stellar contract ID as a strkey, or hex string, ex. * `000000000000000000000000000000000000000000000000000000000000000001`. + * @param {'hex'|'strkey'} format - format of output, defaults to 'strkey' * @returns {string} */ - contractId() { - return this._id.toString('hex'); + contractId(format = 'strkey') { + switch (format) { + case 'strkey': + return StrKey.encodeContract(this._id); + case 'hex': + return this._id.toString('hex'); + default: + throw new Error(`Invalid format: ${format}`); + } } /** diff --git a/test/unit/contract_test.js b/test/unit/contract_test.js index 4d806411..a816ac87 100644 --- a/test/unit/contract_test.js +++ b/test/unit/contract_test.js @@ -1,20 +1,42 @@ -describe('Contract.getFootprint', function () { - it('includes the correct contract code footprint', function () { - let contractId = '0'.repeat(63) + '1'; +describe('Contract', function () { + describe('constructor', function () { + it('parses strkeys', function () { + let contractId = 'CA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAXE'; + let contract = new StellarBase.Contract(contractId); + expect(contract.contractId('strkey')).to.equal(contractId); + }); - let contract = new StellarBase.Contract(contractId); - expect(contract.contractId()).to.equal(contractId); + it('parses hex addresses', function () { + let contractId = '0'.repeat(63) + '1'; + let contract = new StellarBase.Contract(contractId); + expect(contract.contractId('hex')).to.equal(contractId); + }); - const fp = contract.getFootprint(); + it('parses throws on invalid ids', function () { + expect(() => { + new StellarBase.Contract('foobar') + }).to.throw(); + }); + }); + + describe('getFootprint', function () { + it('includes the correct contract code footprint', function () { + let contractId = '0'.repeat(63) + '1'; + + let contract = new StellarBase.Contract(contractId); + expect(contract.contractId('hex')).to.equal(contractId); + + const fp = contract.getFootprint(); - let expected = new StellarBase.xdr.LedgerKey.contractData( - new StellarBase.xdr.LedgerKeyContractData({ - contractId: Buffer.from(contractId, 'hex'), - key: StellarBase.xdr.ScVal.scvLedgerKeyContractExecutable() - }) - ) - .toXDR() - .toString('base64'); - expect(fp.toXDR().toString('base64')).to.equal(expected); + let expected = new StellarBase.xdr.LedgerKey.contractData( + new StellarBase.xdr.LedgerKeyContractData({ + contractId: Buffer.from(contractId, 'hex'), + key: StellarBase.xdr.ScVal.scvLedgerKeyContractExecutable() + }) + ) + .toXDR() + .toString('base64'); + expect(fp.toXDR().toString('base64')).to.equal(expected); + }); }); }); diff --git a/types/index.d.ts b/types/index.d.ts index 15b85c48..fbcc9934 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -27,7 +27,7 @@ export class Address { export class Contract { constructor(contractId: string); - contractId(): string; + contractId(format?: 'hex' | 'strkey'): string; call(method: string, ...params: xdr.ScVal[]): xdr.Operation; }