-
Notifications
You must be signed in to change notification settings - Fork 138
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'soroban' into tx-cloner
- Loading branch information
Showing
9 changed files
with
297 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
import xdr from './xdr'; | ||
|
||
/** | ||
* Supports building {@link xdr.SorobanTransactionData} structures with various | ||
* items set to specific values. | ||
* | ||
* This is recommended for when you are building | ||
* {@link Operation.bumpFootprintExpiration} / | ||
* {@link Operation.restoreFootprint} operations to avoid (re)building the entire | ||
* data structure from scratch. | ||
* | ||
* @constructor | ||
* | ||
* @param {string | xdr.SorobanTransactionData} [sorobanData] either a | ||
* base64-encoded string that represents an | ||
* {@link xdr.SorobanTransactionData} instance or an XDR instance itself | ||
* (it will be copied); if omitted, it starts with an empty instance | ||
* | ||
* @example | ||
* // You want to use an existing data blob but override specific parts. | ||
* const newData = new SorobanDataBuilder(existing) | ||
* .setReadOnly(someLedgerKeys) | ||
* .setRefundableFee("1000") | ||
* .build(); | ||
* | ||
* // You want an instance from scratch | ||
* const newData = new SorobanDataBuilder() | ||
* .setFootprint([someLedgerKey], []) | ||
* .setRefundableFee("1000") | ||
* .build(); | ||
*/ | ||
export class SorobanDataBuilder { | ||
_data; | ||
|
||
constructor(sorobanData) { | ||
let data; | ||
|
||
if (typeof sorobanData === 'string' || ArrayBuffer.isView(sorobanData)) { | ||
data = SorobanDataBuilder.fromXDR(sorobanData); | ||
} else if (!sorobanData) { | ||
data = new xdr.SorobanTransactionData({ | ||
resources: new xdr.SorobanResources({ | ||
footprint: new xdr.LedgerFootprint({ readOnly: [], readWrite: [] }), | ||
instructions: 0, | ||
readBytes: 0, | ||
writeBytes: 0, | ||
extendedMetaDataSizeBytes: 0 | ||
}), | ||
ext: new xdr.ExtensionPoint(0), | ||
refundableFee: new xdr.Int64(0) | ||
}); | ||
} else { | ||
data = xdr.SorobanTransactionData.fromXDR(sorobanData.toXDR()); // copy | ||
} | ||
|
||
this._data = data; | ||
} | ||
|
||
/** | ||
* Decodes and builds a {@link xdr.SorobanTransactionData} instance. | ||
* @param {Uint8Array|Buffer|string} data raw input to decode | ||
* @returns {xdr.SorobanTransactionData} | ||
*/ | ||
static fromXDR(data) { | ||
return xdr.SorobanTransactionData.fromXDR( | ||
data, | ||
typeof data === 'string' ? 'base64' : 'raw' | ||
); | ||
} | ||
|
||
/** | ||
* Sets the "refundable" fee portion of the Soroban data. | ||
* @param {number | bigint | string} fee the refundable fee to set (int64) | ||
* @returns {SorobanDataBuilder} | ||
*/ | ||
setRefundableFee(fee) { | ||
this._data.refundableFee(new xdr.Int64(fee)); | ||
return this; | ||
} | ||
|
||
/** | ||
* Sets up the resource metrics. | ||
* | ||
* You should almost NEVER need this, as its often generated / provided to you | ||
* by transaction simulation/preflight from a Soroban RPC server. | ||
* | ||
* @param {number} cpuInstrs number of CPU instructions | ||
* @param {number} readBytes number of bytes being read | ||
* @param {number} writeBytes number of bytes being written | ||
* @param {number} metadataBytes number of extended metadata bytes | ||
* | ||
* @returns {SorobanDataBuilder} | ||
*/ | ||
setResources(cpuInstrs, readBytes, writeBytes, metadataBytes) { | ||
this._data.resources().instructions(cpuInstrs); | ||
this._data.resources().readBytes(readBytes); | ||
this._data.resources().writeBytes(writeBytes); | ||
this._data.resources().extendedMetaDataSizeBytes(metadataBytes); | ||
|
||
return this; | ||
} | ||
|
||
/** | ||
* Sets the storage access footprint to be a certain set of ledger keys. | ||
* | ||
* You can also set each field explicitly via | ||
* {@link SorobanDataBuilder.setReadOnly} and | ||
* {@link SorobanDataBuilder.setReadWrite}. | ||
* | ||
* Passing `null|undefined` to either parameter will IGNORE the existing | ||
* values. If you want to clear them, pass `[]`, instead. | ||
* | ||
* @param {xdr.LedgerKey[]|null} [readOnly] the set of ledger keys to set in | ||
* the read-only portion of the transaction's `sorobanData` | ||
* @param {xdr.LedgerKey[]|null} [readWrite] the set of ledger keys to set in | ||
* the read-write portion of the transaction's `sorobanData` | ||
* | ||
* @returns {SorobanDataBuilder} | ||
*/ | ||
setFootprint(readOnly, readWrite) { | ||
if (readOnly !== null) { | ||
// null means "leave me alone" | ||
this.setReadOnly(readOnly); | ||
} | ||
if (readWrite !== null) { | ||
this.setReadWrite(readWrite); | ||
} | ||
return this; | ||
} | ||
|
||
/** | ||
* @param {xdr.LedgerKey[]} readOnly read-only keys in the access footprint | ||
* @returns {SorobanDataBuilder} | ||
*/ | ||
setReadOnly(readOnly) { | ||
this._data | ||
.resources() | ||
.footprint() | ||
.readOnly(readOnly ?? []); | ||
return this; | ||
} | ||
|
||
/** | ||
* @param {xdr.LedgerKey[]} readWrite read-write keys in the access footprint | ||
* @returns {SorobanDataBuilder} | ||
*/ | ||
setReadWrite(readWrite) { | ||
this._data | ||
.resources() | ||
.footprint() | ||
.readWrite(readWrite ?? []); | ||
return this; | ||
} | ||
|
||
/** | ||
* @returns {xdr.SorobanTransactionData} a copy of the final data structure | ||
*/ | ||
build() { | ||
return xdr.SorobanTransactionData.fromXDR(this._data.toXDR()); // clone | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
let xdr = StellarBase.xdr; | ||
let dataBuilder = StellarBase.SorobanDataBuilder; | ||
|
||
describe('SorobanTransactionData can be built', function () { | ||
const address = new StellarBase.Address( | ||
StellarBase.Keypair.random().publicKey() | ||
); | ||
|
||
const sentinel = new xdr.SorobanTransactionData({ | ||
resources: new xdr.SorobanResources({ | ||
footprint: new xdr.LedgerFootprint({ readOnly: [], readWrite: [] }), | ||
instructions: 1, | ||
readBytes: 2, | ||
writeBytes: 3, | ||
extendedMetaDataSizeBytes: 4 | ||
}), | ||
ext: new xdr.ExtensionPoint(0), | ||
refundableFee: new xdr.Int64(5) | ||
}); | ||
|
||
const key = xdr.LedgerKey.contractData( | ||
new xdr.LedgerKeyContractData({ | ||
contract: address.toScAddress(), | ||
key: address.toScVal(), | ||
durability: xdr.ContractDataDurability.persistent(), | ||
bodyType: xdr.ContractEntryBodyType.dataEntry() | ||
}) | ||
); | ||
|
||
it('constructs from xdr, base64, and nothing', function () { | ||
new dataBuilder(); | ||
const fromRaw = new dataBuilder(sentinel).build(); | ||
const fromStr = new dataBuilder(sentinel.toXDR('base64')).build(); | ||
|
||
expect(fromRaw).to.eql(sentinel); | ||
expect(fromStr).to.eql(sentinel); | ||
}); | ||
|
||
it('sets properties as expected', function () { | ||
expect( | ||
new dataBuilder().setResources(1, 2, 3, 4).setRefundableFee(5).build() | ||
).to.eql(sentinel); | ||
|
||
// this isn't a valid param but we're just checking that setters work | ||
const withFootprint = new dataBuilder().setFootprint([key], [key]).build(); | ||
expect(withFootprint.resources().footprint().readOnly()[0]).to.eql(key); | ||
expect(withFootprint.resources().footprint().readWrite()[0]).to.eql(key); | ||
}); | ||
|
||
it('leaves untouched footprints untouched', function () { | ||
const builder = new dataBuilder(); | ||
|
||
const data = builder.setFootprint([key], [key]).build(); | ||
const data2 = new dataBuilder(data).setFootprint(null, []).build(); | ||
|
||
expect(data.resources().footprint().readOnly()).to.eql([key]); | ||
expect(data.resources().footprint().readWrite()).to.eql([key]); | ||
expect(data2.resources().footprint().readOnly()).to.eql([key]); | ||
expect(data2.resources().footprint().readWrite()).to.eql([]); | ||
}); | ||
|
||
it('makes copies on build()', function () { | ||
const builder = new dataBuilder(); | ||
const first = builder.build(); | ||
const second = builder.setRefundableFee(100).build(); | ||
|
||
expect(first.refundableFee()).to.not.eql(second.refundableFee()); | ||
}); | ||
}); |
Oops, something went wrong.