From 95b7af7622a1d1ef06df091631ebbc12ee274730 Mon Sep 17 00:00:00 2001 From: Chris Hatch Date: Tue, 19 Sep 2023 23:07:26 +0700 Subject: [PATCH] upgrade to latest soroban client to fix errors getting contract data (seems the interface / XDR has changed ...) implement operation claim_claimable_balance improve rendering of ScVals - handle bigints nested in objects --- .../operations/ClaimableBalances.tsx | 13 ++++++- app/components/operations/Operation.tsx | 3 +- app/lib/stellar/contracts.ts | 24 +++++++----- app/lib/stellar/server.ts | 4 +- app/lib/stellar/xdr_scval_utils.ts | 39 ++++++++++++++++--- app/routes/account.$accountId.tsx | 2 +- app/routes/ledger.$ledgerId.tsx | 2 +- app/routes/tx.$txHash.tsx | 2 +- package-lock.json | 16 ++++---- package.json | 2 +- 10 files changed, 76 insertions(+), 31 deletions(-) diff --git a/app/components/operations/ClaimableBalances.tsx b/app/components/operations/ClaimableBalances.tsx index cd9ef8838..02931b86d 100644 --- a/app/components/operations/ClaimableBalances.tsx +++ b/app/components/operations/ClaimableBalances.tsx @@ -5,8 +5,17 @@ const CreateClaimableBalanceOperation = ({ amount, sponsor, asset }: any) => { const [assetCode, assetIssuer] = asset.split(':') return ( - created claimable balance for {amount} + created claimable balance for {amount} + ) } -export { CreateClaimableBalanceOperation } \ No newline at end of file +const ClaimClaimableBalanceOperation = ({ claimant }: any) => { + return ( + + Claimant + + ) +} + +export { ClaimClaimableBalanceOperation, CreateClaimableBalanceOperation } \ No newline at end of file diff --git a/app/components/operations/Operation.tsx b/app/components/operations/Operation.tsx index 164904d6d..b25edac38 100644 --- a/app/components/operations/Operation.tsx +++ b/app/components/operations/Operation.tsx @@ -10,7 +10,7 @@ import AccountMerge from "./AccountMerge" import AllowTrust, { AllowTrustProps } from "./AllowTrust" import BumpSequence from "./BumpSequence" import ChangeTrust from "./ChangeTrust" -import { CreateClaimableBalanceOperation } from "./ClaimableBalances" +import { ClaimClaimableBalanceOperation, CreateClaimableBalanceOperation } from "./ClaimableBalances" import CreateAccount, { CreateAccountProps } from "./CreateAccount" import Inflation from "./Inflation" import InvokeHostFunction from "./InvokeHostFunction" @@ -31,6 +31,7 @@ const OperationTypeToComponentMap = { create_passive_sell_offer: Offer, create_passive_offer: Offer, // < Protocol 11 + claim_claimable_balance: ClaimClaimableBalanceOperation, create_claimable_balance: CreateClaimableBalanceOperation, inflation: Inflation, diff --git a/app/lib/stellar/contracts.ts b/app/lib/stellar/contracts.ts index 0dafbc623..8bf945439 100644 --- a/app/lib/stellar/contracts.ts +++ b/app/lib/stellar/contracts.ts @@ -38,19 +38,25 @@ const getContractInfo = async ( new xdr.LedgerKeyContractData({ contract: new Contract(contractId).address().toScAddress(), key: xdr.ScVal.scvLedgerKeyContractInstance(), - durability: xdr.ContractDataDurability.persistent(), - bodyType: xdr.ContractEntryBodyType.dataEntry() + durability: xdr.ContractDataDurability.persistent() }) ) - const ledgerEntries = await server.getLedgerEntries([ledgerKey]) + let ledgerEntries + try { + + ledgerEntries = await server.getLedgerEntries(ledgerKey) + } catch (error) { + console.error(error) + } + if (ledgerEntries == null || ledgerEntries.entries == null) { return null } const ledgerEntry = ledgerEntries.entries[0] const codeData = xdr.LedgerEntryData.fromXDR(ledgerEntry.xdr, 'base64') - .contractData().body().data() + .contractData() const wasmIdLedger = ledgerEntry.lastModifiedLedgerSeq @@ -69,11 +75,10 @@ const getContractCode = async ( ): Promise<{ wasmCode: string, wasmCodeLedger: number } | null> => { const ledgerKey = xdr.LedgerKey.contractCode( new xdr.LedgerKeyContractCode({ - hash: wasmId, - bodyType: xdr.ContractEntryBodyType.dataEntry() + hash: wasmId }) ) - const ledgerEntries = await server.getLedgerEntries([ledgerKey]) + const ledgerEntries = await server.getLedgerEntries(ledgerKey) if (ledgerEntries == null || ledgerEntries.entries == null) { return null } @@ -82,7 +87,7 @@ const getContractCode = async ( const wasmCodeLedger = ledgerEntry.lastModifiedLedgerSeq as number const codeEntry = xdr.LedgerEntryData.fromXDR(ledgerEntry.xdr, 'base64') - const wasmCode = codeEntry.contractCode().body().code().toString('hex') + const wasmCode = codeEntry.contractCode().code().toString('hex') return { wasmCode, wasmCodeLedger } } @@ -111,8 +116,9 @@ const loadContract = async ( } const { wasmId, wasmIdLedger } = wasmIdResult + // console.log(`wasmIdResult ${JSON.stringify(wasmIdResult, null, 2)}`) if (!wasmId) { - console.error('Failed to get wasm id') + console.warn('no wasmId not in result') return } diff --git a/app/lib/stellar/server.ts b/app/lib/stellar/server.ts index dbfed5b47..d3d58c7a7 100644 --- a/app/lib/stellar/server.ts +++ b/app/lib/stellar/server.ts @@ -29,8 +29,8 @@ const requestToServer = (request: Request): HorizonServer => { const requestToSorobanServer = (request: Request): SorobanServer => { const { networkType } = requestToNetworkDetails(request) - if (![networks.future, networks.local].includes(networkType)) { - throw new Error(`network ${networkType} not yet supported by Soroban / Stellar Explorer`) + if (![networks.future, networks.test, networks.local].includes(networkType)) { + throw new Error(`Soroban smart contracts not yet supported on the network [${networkType}].`) } return new SorobanServer( networkType, diff --git a/app/lib/stellar/xdr_scval_utils.ts b/app/lib/stellar/xdr_scval_utils.ts index 1527e213e..517b5ad29 100644 --- a/app/lib/stellar/xdr_scval_utils.ts +++ b/app/lib/stellar/xdr_scval_utils.ts @@ -1,5 +1,7 @@ import { StrKey, scValToNative, xdr } from 'soroban-client' +const Buffer = require('buffer').Buffer + export function scValToAddress( scval: any // : SorobanClient.xdr.ScVal | undefined ): string { @@ -14,17 +16,44 @@ export function scValToAddress( } } -export const scValToString = (scVal: any) => { - console.log(`type ${scVal.switch ? scVal.switch().name : 'unknown'}`) - const native = scValToNative(scVal) - console.log(`type: ${typeof native} val: ${native}`) +/** + * Takes a value returned by scValToNative (some js type) and converts it to a string. + * + * Handles: + * - bigint - JSON.stringify can't handle it + * - Buffer - ensure hex string returned + * - values nested in objects or arrays + * + * @param native some javascript value to be converted to a string + * @returns string representation + */ +const jsNativeValToString = (native: any): string => { + // console.log(`type=${typeof native}`) if (typeof native === 'string') { return native } else if (typeof native === 'bigint') { return native.toString() } else if (Array.isArray(native)) { - return native.map(val => typeof val === 'bigint' ? val.toString() : val) + return JSON.stringify(native.map(val => jsNativeValToString(val))) + } else if (typeof native === 'object') { + return jsNativeObjectValToString(native) + } else if (Buffer.isBuffer(native)) { + return native.toString('hex') } else { return JSON.stringify(native) } +} + +const jsNativeObjectValToString = (native: any): string => { + const convertedObj: Record = {} + for (const [objKey, objVal] of Object.entries(native)) { + // console.log(`key ${JSON.stringify(objKey, null, 2)}`) + convertedObj[objKey] = jsNativeValToString(objVal) + } + return JSON.stringify(convertedObj) +} + +export const scValToString = (scVal: any): string => { + const native = scValToNative(scVal) + return jsNativeValToString(native) } \ No newline at end of file diff --git a/app/routes/account.$accountId.tsx b/app/routes/account.$accountId.tsx index 7c0df11f5..fb6e587a7 100644 --- a/app/routes/account.$accountId.tsx +++ b/app/routes/account.$accountId.tsx @@ -177,7 +177,7 @@ export const loader = async ({ params, request }: LoaderArgs) => { if (error instanceof NotFoundError) { throw new Response(null, { status: 404, - statusText: `Account not found`, + statusText: `Account [${params.accountId}] not found on this network.`, }) } else if (error instanceof AccountTypeUnrecognizedException) { throw new Response(null, { diff --git a/app/routes/ledger.$ledgerId.tsx b/app/routes/ledger.$ledgerId.tsx index e00fbcaff..a0c47183e 100644 --- a/app/routes/ledger.$ledgerId.tsx +++ b/app/routes/ledger.$ledgerId.tsx @@ -37,7 +37,7 @@ export const loader = async ({ params, request }: LoaderArgs) => { const ledgerSeq = params.ledgerId as string return Promise.all([ ledger(server, ledgerSeq), - transactions(server, { ledgerSeq }), + transactions(server, { ledgerSeq, limit: 100 }), server.serverURL.toString() ]).then(json) } diff --git a/app/routes/tx.$txHash.tsx b/app/routes/tx.$txHash.tsx index 8a3172fc2..d430318c6 100644 --- a/app/routes/tx.$txHash.tsx +++ b/app/routes/tx.$txHash.tsx @@ -47,7 +47,7 @@ export const loader = ({ params, request }: LoaderArgs) => { const server = requestToServer(request) return Promise.all([ transaction(server, params.txHash as string), - operations(server, { tx: params.txHash, limit: 10 }), + operations(server, { tx: params.txHash, limit: 100 }), server.serverURL.toString() ]).then(json) } diff --git a/package-lock.json b/package-lock.json index 4c1e4cc49..74a6f40ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,7 @@ "react-json-pretty": "^2.2.0", "react-router-bootstrap": "^0.26.2", "remix-utils": "^6.6.0", - "soroban-client": "0.11.2", + "soroban-client": "1.0.0-beta.2", "stellar-sdk": "10.4.1", "urijs": "1.19.11" }, @@ -12304,14 +12304,14 @@ } }, "node_modules/soroban-client": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/soroban-client/-/soroban-client-0.11.2.tgz", - "integrity": "sha512-cc8d3YlaNzWR1prOggOlt2BmhWiHWgxebxavs7l2QOhzyG5qN2zQGnbCprVeMlOsY/tsI51liEEHAmtLROphqA==", + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/soroban-client/-/soroban-client-1.0.0-beta.2.tgz", + "integrity": "sha512-v5h3yvef7HkUD3H26w33NUEgRXcPiOSDWEsVzMloaxsprs3N002tXJHvFF+Uw1eYt50Uk6bvqBgvkLwX10VENw==", "dependencies": { "axios": "^1.4.0", "bignumber.js": "^9.1.1", "buffer": "^6.0.3", - "stellar-base": "10.0.0-soroban.8", + "stellar-base": "v10.0.0-beta.1", "urijs": "^1.19.1" } }, @@ -12336,9 +12336,9 @@ } }, "node_modules/soroban-client/node_modules/stellar-base": { - "version": "10.0.0-soroban.8", - "resolved": "https://registry.npmjs.org/stellar-base/-/stellar-base-10.0.0-soroban.8.tgz", - "integrity": "sha512-mtj+4EcCnp4ZyH2FzRl62/DAstTXOddHVRZdzFQ94WgyQz2yVNzt+ANDS1D/7ku4d2mIzoJIj9l0/H0A5nRgXQ==", + "version": "10.0.0-beta.1", + "resolved": "https://registry.npmjs.org/stellar-base/-/stellar-base-10.0.0-beta.1.tgz", + "integrity": "sha512-zXC5AsbUsLi57JruyeIMv23s3iUxq/P2ZFrSJ+FerLIZjSAjY8EDs4zwY4LCuu7swUu46Lm8GK6sqxUZCPekHw==", "dependencies": { "base32.js": "^0.1.0", "bignumber.js": "^9.1.2", diff --git a/package.json b/package.json index 8c423771b..b9d473a07 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "react-json-pretty": "^2.2.0", "react-router-bootstrap": "^0.26.2", "remix-utils": "^6.6.0", - "soroban-client": "0.11.2", + "soroban-client": "1.0.0-beta.2", "stellar-sdk": "10.4.1", "urijs": "1.19.11" },