Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update client to support Preview 10 #103

Merged
merged 19 commits into from
Jul 3, 2023
Merged
Show file tree
Hide file tree
Changes from 8 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
3 changes: 3 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,6 @@ jobs:

- name: Browser Tests
run: yarn test:browser

- name: Run Prettier Checks
run: yarn fmt && (git diff-index --quiet HEAD; git diff)
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,24 @@ A breaking change should be clearly marked in this log.

## Unreleased

## v0.9.0

### Updated
* `Server.getContractData` has an additional, optional parameter: `expirationType?: string` which should be set to either `'temporary'` or `'persistent'` depending on the type of ledger key. By default, it will attempt to fetch both, returning whichever one it finds ([#103](https://github.com/stellar/js-soroban-client/pull/103)).
* The XDR library (`stellar-base`) has been upgraded to Preview 10's protocol format. This includes the following changes:

#### Breaking Changes

- Many XDR structures have changed, please refer to the `types/next.d.ts` diff for details ([#633](https://github.com/stellar/js-stellar-base/pull/633)).
- We have returned to the world in which **one** transaction contains **one** operation which contains **one** host function invocation. This means `Operation.invokeHostFunctions` is gone and `Operation.invokeHostFunction` has changed to accept `{ func, auth }`, where `func` is the correct `xdr.HostFunction` and `auth` is a list of `xdr.SorobanAuthorizationEntry` items that outline the authorization tree for the call stack ([#633](https://github.com/stellar/js-stellar-base/pull/633)). Better abstractions for creating an `xdr.HostFunction` are forthcoming, though you can still refer to `Contract.call()` for help.

#### Added

- A new abstraction for dealing with large integers and `ScVal`s: see `ScInt`, `XdrLargeInt`, and `scValToBigInt` ([#620](https://github.com/stellar/js-stellar-base/pull/620)).
- A new abstraction for converting between native JavaScript types and complex `ScVal`s: see `nativeToScVal` and `scValToNative` ([#630](https://github.com/stellar/js-stellar-base/pull/630)).
- We have added two new operations related to state expiration in Soroban: `BumpFootprintExpiration` and `RestoreFootprint`. Please refer to their docstrings for details ([#633](https://github.com/stellar/js-stellar-base/pull/633)).



## v0.8.1

Expand Down
44 changes: 22 additions & 22 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "soroban-client",
"version": "0.8.1",
"version": "0.9.0",
"description": "A library for working with Stellar's Soroban RPC servers.",
"author": "Stellar Development Foundation <[email protected]>",
"homepage": "https://github.com/stellar/js-soroban-client",
Expand Down Expand Up @@ -29,7 +29,7 @@
"build:prod": "cross-env NODE_ENV=production yarn _build",
"build:node": "yarn _babel && yarn build:ts",
"build:ts": "tsc -p ./tsconfig.json",
"build:browser": "webpack --stats-modules-space 999 -c ./webpack.config.browser.js",
"build:browser": "webpack -c ./webpack.config.browser.js",
"build:docs": "cross-env NODE_ENV=docs yarn _babel",
"clean": "rm -rf lib/ dist/ coverage/ .nyc_output/ jsdoc/",
"docs": "yarn build:docs && jsdoc -c .jsdoc.json --verbose",
Expand Down Expand Up @@ -76,31 +76,31 @@
]
},
"devDependencies": {
"@babel/cli": "^7.21.5",
"@babel/core": "^7.22.1",
"@babel/eslint-parser": "^7.21.8",
"@babel/eslint-plugin": "^7.19.1",
"@babel/preset-env": "^7.22.4",
"@babel/preset-typescript": "^7.21.5",
"@babel/register": "^7.21.0",
"@definitelytyped/dtslint": "^0.0.162",
"@babel/cli": "^7.22.5",
"@babel/core": "^7.22.5",
"@babel/eslint-parser": "^7.22.5",
"@babel/eslint-plugin": "^7.22.5",
"@babel/preset-env": "^7.22.5",
"@babel/preset-typescript": "^7.22.5",
"@babel/register": "^7.22.5",
"@definitelytyped/dtslint": "^0.0.163",
"@istanbuljs/nyc-config-babel": "3.0.0",
"@stellar/tsconfig": "^1.0.2",
"@types/detect-node": "^2.0.0",
"@types/eventsource": "^1.1.2",
"@types/lodash": "^4.14.192",
"@types/node": "^20.2.5",
"@types/node": "^20.3.2",
"@types/randombytes": "^2.0.0",
"@types/urijs": "^1.19.6",
"@typescript-eslint/parser": "^5.59.8",
"axios-mock-adapter": "^1.21.4",
"@typescript-eslint/parser": "^5.60.1",
"axios-mock-adapter": "^1.21.5",
"babel-loader": "^9.1.2",
"babel-plugin-istanbul": "^6.1.1",
"chai": "^4.3.7",
"chai-as-promised": "^7.1.1",
"chai-http": "^4.3.0",
"chai-http": "^4.4.0",
"cross-env": "^7.0.3",
"eslint": "^8.40.0",
"eslint": "^8.43.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-import": "^2.25.2",
Expand All @@ -114,27 +114,27 @@
"karma": "^6.4.2",
"karma-chai": "^0.1.0",
"karma-chrome-launcher": "^3.1.0",
"karma-coverage": "^2.2.0",
"karma-coverage": "^2.2.1",
"karma-firefox-launcher": "^2.1.1",
"karma-mocha": "^2.0.0",
"karma-sinon-chai": "^2.0.2",
"karma-webpack": "^5.0.0",
"lint-staged": "^13.2.2",
"lint-staged": "^13.2.3",
"minami": "^1.1.1",
"mocha": "^10.2.0",
"node-polyfill-webpack-plugin": "^2.0.1",
"nyc": "^15.1.0",
"prettier": "^2.8.8",
"randombytes": "^2.1.0",
"sinon": "^15.0.3",
"sinon": "^15.2.0",
"sinon-chai": "^3.7.0",
"taffydb": "^2.7.3",
"terser-webpack-plugin": "^5.3.8",
"ts-node": "^10.9.1",
"typescript": "^5.0.4",
"typescript": "^5.1.5",
"utility-types": "^3.7.0",
"webpack": "^5.85.0",
"webpack-cli": "^5.1.1"
"webpack": "^5.88.1",
"webpack-cli": "^5.1.4"
},
"dependencies": {
"axios": "^1.4.0",
Expand All @@ -145,7 +145,7 @@
"eventsource": "^2.0.2",
"lodash": "^4.17.21",
"randombytes": "^2.1.0",
"stellar-base": "9.0.0-soroban.3",
"stellar-base": "10.0.0-soroban.1",
"toml": "^3.0.0",
"urijs": "^1.19.1"
}
Expand Down
123 changes: 98 additions & 25 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,24 @@ export interface GetEventsRequest {
}

/**
* Server handles the network connection to a [Soroban-RPC](https://soroban.stellar.org/docs)
* instance and exposes an interface for requests to that instance.
* Server handles the network connection to a
* [Soroban-RPC](https://soroban.stellar.org/docs) instance and exposes an
* interface for requests to that instance.
*
* @constructor
* @param {string} serverURL Soroban-RPC Server URL (ex. `http://localhost:8000/soroban/rpc`).
*
* @param {string} serverURL Soroban-RPC Server URL (ex.
* `http://localhost:8000/soroban/rpc`).
* @param {object} [opts] Options object
* @param {boolean} [opts.allowHttp] - Allow connecting to http servers, default: `false`. This must be set to false in production deployments! You can also use {@link Config} class to set this globally.
* @param {string} [opts.appName] - Allow set custom header `X-App-Name`, default: `undefined`.
* @param {string} [opts.appVersion] - Allow set custom header `X-App-Version`, default: `undefined`.
* @param {boolean} [opts.allowHttp] - Allow connecting to http servers,
* default: `false`. This must be set to false in production deployments! You
* can also use {@link Config} class to set this globally.
* @param {string} [opts.appName] - Allow set custom header `X-App-Name`
* @param {string} [opts.appVersion] - Allow set custom header `X-App-Version`
*/
export class Server {
/**
* serverURL Soroban-RPC Server URL (ex. `http://localhost:8000/soroban/rpc`).
* Soroban-RPC Server URL (ex. `http://localhost:8000/soroban/rpc`).
*/
public readonly serverURL: URI;

Expand Down Expand Up @@ -141,43 +147,107 @@ export class Server {
* console.log("latestLedger:", data.latestLedger);
* });
*
* Allows you to directly inspect the current state of a contract. This is a backup way to access your contract data which may not be available via events or simulateTransaction.
* Allows you to directly inspect the current state of a contract. This is a
* backup way to access your contract data which may not be available via
* events or simulateTransaction.
*
* @param {string} contractId - The contract ID containing the data to load. Encoded as Stellar Contract Address e.g. `CCJZ5DGASBWQXR5MPFCJXMBI333XE5U3FSJTNQU7RIKE3P5GN2K2WYD5` or a hex string for backwards compatibility, but will likely be deprecated in the future.
* By default, we will request both the 'temporary' and 'persistent' versions
* of the ledger durability. Only one should exist.
*
* @param {string} contractId - The contract ID containing the data to load.
* Encoded as Stellar Contract Address e.g.
* `CCJZ5DGASBWQXR5MPFCJXMBI333XE5U3FSJTNQU7RIKE3P5GN2K2WYD5` or a hex
* string for backwards compatibility, but will likely be deprecated in the
* future.
* @param {xdr.ScVal} key - The key of the contract data to load.
* @returns {Promise<SorobanRpc.LedgerEntryResult>} Returns a promise to the {@link SorobanRpc.LedgerEntryResult} object with the current value.
* @param {string} [expirationType] - The "durability keyspace" that this
* ledger key belongs to. By default, it tries requesting both the
* 'temporary' and 'persistent' versions so that you don't need to track
* this separately.
*
* WARNING: In the default case, only one should *actually* exist! If BOTH
* exist, this function will throw as it cannot distinguish between them.
*
* @returns {Promise<SorobanRpc.LedgerEntryResult>} Returns a promise to the
* {@link SorobanRpc.LedgerEntryResult} object with the current value.
*
* @warning If the data entry in question is a 'temporary' entry, it's
* entirely possible that it has expired out of existence. Future versions
* of this client may provide abstractions to handle that.
*/
public async getContractData(
contractId: string,
Shaptic marked this conversation as resolved.
Show resolved Hide resolved
key: xdr.ScVal,
expirationType?: 'temporary' | 'persistent'
Shaptic marked this conversation as resolved.
Show resolved Hide resolved
): Promise<SorobanRpc.LedgerEntryResult> {
const contractKey = xdr.LedgerKey.contractData(
new xdr.LedgerKeyContractData({
contractId: new Contract(contractId).address().toBuffer(),
key,
}),
).toXDR("base64");
const getLedgerEntriesResponse: SorobanRpc.GetLedgerEntriesResponse = await jsonrpc.post(
//
// We query for both the temporary and persistent versions by default so that
// the user doesn't have to care about the "durability keyspace" of the
// ledger key, but we expect only one of them to work.
//
const [ persistentKey, temporaryKey ] = [
xdr.LedgerKey.contractData(
new xdr.LedgerKeyContractData({
contract: new Contract(contractId).address().toScAddress(),
key,
durability: xdr.ContractDataDurability.persistent(),
bodyType: xdr.ContractEntryBodyType.dataEntry() // expirationExtension is internal
})
).toXDR("base64"),
xdr.LedgerKey.contractData(
new xdr.LedgerKeyContractData({
contract: new Contract(contractId).address().toScAddress(),
key,
durability: xdr.ContractDataDurability.temporary(),
bodyType: xdr.ContractEntryBodyType.dataEntry()
})
).toXDR("base64"),
];

let requested: string[];
switch (expirationType) {
case undefined:
requested = [persistentKey, temporaryKey];
break;

case 'temporary':
requested = [temporaryKey];
break;

case 'persistent':
requested = [persistentKey];
break;
}

const getLedgerEntriesResponse = await jsonrpc.post<
SorobanRpc.GetLedgerEntriesResponse
>(
paulbellamy marked this conversation as resolved.
Show resolved Hide resolved
this.serverURL.toString(),
"getLedgerEntries",
[contractKey],
requested,
);

if (getLedgerEntriesResponse.entries.length === 0) {
return Promise.reject({
code: 404,
message: "Ledger entry not found. Key: " + contractKey,
message: `Ledger entry not found. Key(s): [${requested.join(', ')}]`
});
} else if (getLedgerEntriesResponse.entries.length > 1) {
return Promise.reject({
code: 409,
message: `Multiple ledger entries found, specify keyspace. Key(s): [${requested.join(', ')}]`,
})
}

return getLedgerEntriesResponse.entries[0];
}

/**
* Reads the current value of ledger entries directly.
*
* Allows you to directly inspect the current state of contracts,
* contract's code, or any other ledger entries. This is a backup way to access
* your contract data which may not be available via events or
* simulateTransaction.
* Allows you to directly inspect the current state of contracts, contract's
* code, or any other ledger entries. This is a backup way to access your
* contract data which may not be available via events or simulateTransaction.
*
* To fetch contract wasm byte-code, use the ContractCode ledger entry key.
*
Expand All @@ -196,7 +266,10 @@ export class Server {
* });
*
* @param {xdr.ScVal} key - The key of the contract data to load.
* @returns {Promise<SorobanRpc.GetLedgerEntriesResponse>} Returns a promise to the {@link SorobanRpc.GetLedgerEntriesResponse} object with the current value.
*
* @returns {Promise<SorobanRpc.GetLedgerEntriesResponse>} Returns a promise
* to the {@link SorobanRpc.GetLedgerEntriesResponse} object with the
* current value.
*/
public async getLedgerEntries(
keys: xdr.LedgerKey[],
Expand Down Expand Up @@ -469,7 +542,7 @@ export class Server {
if (simResponse.error) {
throw simResponse.error;
}
if (!simResponse.results || simResponse.results.length < 1) {
if (!simResponse.results || simResponse.results.length !== 1) {
throw new Error("transaction simulation failed");
}
return assembleTransaction(transaction, passphrase, simResponse);
Expand Down
6 changes: 3 additions & 3 deletions src/soroban_rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ export namespace SorobanRpc {
}

export interface SimulateHostFunctionResult {
// each string is ContractAuth XDR in base64
auth: string[];
// each string is SorobanAuthorizationEntry XDR in base64
auth?: string[];
// function response as SCVal XDR in base64
xdr: string;
}
Expand All @@ -131,7 +131,7 @@ export namespace SorobanRpc {
transactionData: string;
events: string[];
minResourceFee: string;
results: SimulateHostFunctionResult[];
results: SimulateHostFunctionResult[]; // always one element, tho
Shaptic marked this conversation as resolved.
Show resolved Hide resolved
latestLedger: number;
cost: Cost;
}
Expand Down
Loading
Loading