Skip to content

Commit

Permalink
soroban server improvements: getAccount, getContractData
Browse files Browse the repository at this point in the history
  • Loading branch information
christian-rogobete committed Jun 3, 2024
1 parent a85e34a commit 560ad5f
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 37 deletions.
46 changes: 46 additions & 0 deletions lib/src/soroban/soroban_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import 'dart:convert';
import 'dart:typed_data';
import 'package:dio/dio.dart' as dio;
import 'package:dio/io.dart';
import 'package:stellar_flutter_sdk/src/account.dart';
import 'package:stellar_flutter_sdk/src/key_pair.dart';
import 'package:stellar_flutter_sdk/src/responses/response.dart';
import 'package:stellar_flutter_sdk/src/xdr/xdr_account.dart';
import 'package:stellar_flutter_sdk/src/xdr/xdr_type.dart';
import 'soroban_auth.dart';
import '../xdr/xdr_data_entry.dart';
Expand Down Expand Up @@ -100,6 +103,49 @@ class SorobanServer {
return GetLedgerEntriesResponse.fromJson(response.data);
}

/// Fetches a minimal set of current info about a Stellar account. Needed to get the current sequence
/// number for the account, so you can build a successful transaction.
/// Returns null if account was not found for the given [accountId].
Future<Account?> getAccount(String accountId) async {
XdrLedgerKey ledgerKey = XdrLedgerKey(XdrLedgerEntryType.ACCOUNT);
ledgerKey.account = XdrLedgerKeyAccount(
XdrAccountID(KeyPair.fromAccountId(accountId).xdrPublicKey));
GetLedgerEntriesResponse ledgerEntriesResponse =
await getLedgerEntries([ledgerKey.toBase64EncodedXdrString()]);

if (ledgerEntriesResponse.entries != null &&
ledgerEntriesResponse.entries!.length > 0) {
var accountEntry =
ledgerEntriesResponse.entries![0].ledgerEntryDataXdr.account;
if (accountEntry != null) {
String accountId =
KeyPair.fromXdrPublicKey(accountEntry.accountID.accountID)
.accountId;
int seqNr = accountEntry.seqNum.sequenceNumber.int64;
return Account(accountId, seqNr);
}
}
return null;
}

/// Reads the current value of contract data ledger entries directly.
/// Requires the [contractId] of the contract containing the data to load, the [key] of the contract data to load,
/// The [durability] keyspace that this ledger key belongs to, which is either
/// XdrContractDataDurability.TEMPORARY or XdrContractDataDurability.PERSISTENT
Future<LedgerEntry?> getContractData(String contractId, XdrSCVal key,
XdrContractDataDurability durability) async {
XdrLedgerKey ledgerKey = XdrLedgerKey(XdrLedgerEntryType.CONTRACT_DATA);
ledgerKey.contractData = XdrLedgerKeyContractData(
Address.forContractId(contractId).toXdr(), key, durability);
GetLedgerEntriesResponse ledgerEntriesResponse =
await getLedgerEntries([ledgerKey.toBase64EncodedXdrString()]);
if (ledgerEntriesResponse.entries != null &&
ledgerEntriesResponse.entries!.length > 0) {
return ledgerEntriesResponse.entries![0];
}
return null;
}

/// Loads the contract source code (including source code - wasm bytes) for a given wasm id.
Future<XdrContractCodeEntry?> loadContractCodeForWasmId(String wasmId) async {
XdrLedgerKey ledgerKey = XdrLedgerKey(XdrLedgerEntryType.CONTRACT_CODE);
Expand Down
68 changes: 53 additions & 15 deletions test/soroban_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ void main() {
Future restoreContractFootprint(String contractCodePath) async {
await Future.delayed(Duration(seconds: 5));
// load account
AccountResponse accountA = await sdk.accounts.account(accountAId);
Account? account = await sorobanServer.getAccount(accountAId);
assert(account != null);
Account accountA = account!;

// load contract wasm file
Uint8List contractCode = await Util.readFile(contractCodePath);
Expand Down Expand Up @@ -98,7 +100,10 @@ void main() {
transactionData.resources.footprint.readOnly =
List<XdrLedgerKey>.empty(growable: false);

accountA = await sdk.accounts.account(accountAId);
account = await sorobanServer.getAccount(accountAId);
assert(account != null);
accountA = account!;

RestoreFootprintOperation restoreOp =
RestoreFootprintOperationBuilder().build();
transaction =
Expand Down Expand Up @@ -142,7 +147,9 @@ void main() {
await Future.delayed(Duration(seconds: 5));

// load account
AccountResponse accountA = await sdk.accounts.account(accountAId);
Account? account = await sorobanServer.getAccount(accountAId);
assert(account != null);
Account accountA = account!;

ExtendFootprintTTLOperation bumpFunction =
ExtendFootprintTTLOperationBuilder(extendTo).build();
Expand Down Expand Up @@ -176,7 +183,10 @@ void main() {
assert(simulateResponse.resultError == null);
assert(simulateResponse.transactionData != null);

accountA = await sdk.accounts.account(accountAId);
account = await sorobanServer.getAccount(accountAId);
assert(account != null);
accountA = account!;

// set transaction data, add resource fee and sign transaction
transaction.sorobanTransactionData = simulateResponse.transactionData;
transaction.addResourceFee(simulateResponse.minResourceFee!);
Expand Down Expand Up @@ -252,7 +262,6 @@ void main() {
assert(!latestLedgerResponse.isErrorResponse);
assert(latestLedgerResponse.id != null);
assert(latestLedgerResponse.protocolVersion != null);
// assert(20 == latestLedgerResponse.protocolVersion);
assert(latestLedgerResponse.sequence != null);
});

Expand All @@ -264,7 +273,10 @@ void main() {
test('test upload contract', () async {
await Future.delayed(Duration(seconds: 5));
// load account
AccountResponse accountA = await sdk.accounts.account(accountAId);
//AccountResponse accountA = await sdk.accounts.account(accountAId);
Account? account = await sorobanServer.getAccount(accountAId);
assert(account != null);
Account accountA = account!;

// load contract wasm file
helloContractCode = await Util.readFile(helloContractPath);
Expand Down Expand Up @@ -355,7 +367,9 @@ void main() {
assert(helloContractWasmId != null);

// reload account for current sequence nr
AccountResponse accountA = await sdk.accounts.account(accountAId);
Account? account = await sorobanServer.getAccount(accountAId);
assert(account != null);
Account accountA = account!;

CreateContractHostFunction function = CreateContractHostFunction(
Address.forAccountId(accountAId), helloContractWasmId!);
Expand Down Expand Up @@ -442,7 +456,9 @@ void main() {
assert(helloContractId != null);

// load account for sequence number
AccountResponse accountA = await sdk.accounts.account(accountAId);
Account? account = await sorobanServer.getAccount(accountAId);
assert(account != null);
Account accountA = account!;

// prepare argument
XdrSCVal arg = XdrSCVal.forSymbol("friend");
Expand Down Expand Up @@ -537,12 +553,22 @@ void main() {
} else {
assert(false);
}

await Future.delayed(Duration(seconds: 5));
// test contract data fetching
print(StrKey.encodeContractIdHex(helloContractId!));
LedgerEntry? entry = await sorobanServer.getContractData(
helloContractId!, XdrSCVal.forLedgerKeyContractInstance(),
XdrContractDataDurability.PERSISTENT);
assert(entry != null);
});

test('test events', () async {
await Future.delayed(Duration(seconds: 5));
// Install contract
AccountResponse submitter = await sdk.accounts.account(accountAId);
Account? account = await sorobanServer.getAccount(accountAId);
assert(account != null);
Account submitter = account!;

Uint8List contractCode = await Util.readFile(eventsContractPath);

Expand Down Expand Up @@ -582,7 +608,10 @@ void main() {
await Future.delayed(Duration(seconds: 5));

// Create contract
submitter = await sdk.accounts.account(accountAId);
account = await sorobanServer.getAccount(accountAId);
assert(account != null);
submitter = account!;

operation = InvokeHostFuncOpBuilder(CreateContractHostFunction(
Address.forAccountId(accountAId), wasmId))
.build();
Expand All @@ -599,7 +628,7 @@ void main() {
transaction.setSorobanAuth(simulateResponse.sorobanAuth);
transaction.addResourceFee(simulateResponse.minResourceFee!);
transaction.sign(keyPairA, network);

print("TX-SEP11 " + transaction.toEnvelopeXdrBase64());
sendResponse = await sorobanServer.sendTransaction(transaction);
assert(sendResponse.error == null);
assert(sendResponse.status != SendTransactionResponse.STATUS_ERROR);
Expand All @@ -615,7 +644,9 @@ void main() {

await Future.delayed(Duration(seconds: 5));
// Invoke contract
submitter = await sdk.accounts.account(accountAId);
account = await sorobanServer.getAccount(accountAId);
assert(account != null);
submitter = account!;

String functionName = "increment";
InvokeContractHostFunction hostFunction =
Expand Down Expand Up @@ -707,7 +738,9 @@ void main() {
test('test deploy SAC with source account', () async {
await Future.delayed(Duration(seconds: 5));
// load account
AccountResponse accountA = await sdk.accounts.account(accountAId);
Account? account = await sorobanServer.getAccount(accountAId);
assert(account != null);
Account accountA = account!;

InvokeHostFunctionOperation operation = InvokeHostFuncOpBuilder(
DeploySACWithSourceAccountHostFunction(
Expand Down Expand Up @@ -796,7 +829,10 @@ void main() {
await Future.delayed(Duration(seconds: 5));

// prepare trustline
AccountResponse sourceAccount = await sdk.accounts.account(accountBId);
Account? account = await sorobanServer.getAccount(accountBId);
assert(account != null);
Account sourceAccount = account!;

ChangeTrustOperationBuilder ctOp =
ChangeTrustOperationBuilder(assetFsdk, "1000000");
ctOp.setSourceAccount(accountAId);
Expand All @@ -814,7 +850,9 @@ void main() {
assert(response.success);

// load account
AccountResponse accountB = await sdk.accounts.account(accountBId);
account = await sorobanServer.getAccount(accountBId);
assert(account != null);
Account accountB = account!;

InvokeHostFunctionOperation operation =
InvokeHostFuncOpBuilder(DeploySACWithAssetHostFunction(assetFsdk))
Expand Down
54 changes: 41 additions & 13 deletions test/soroban_test_atomic_swap.dart
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ void main() {
Future<String> installContract(String contractCodePath) async {
await Future.delayed(Duration(seconds: 5));
// load account
AccountResponse submitter = await sdk.accounts.account(adminId);
Account? account = await sorobanServer.getAccount(adminId);
assert(account != null);
Account submitter = account!;

// load contract wasm file
Uint8List contractCode = await Util.readFile(contractCodePath);
Expand Down Expand Up @@ -137,8 +139,11 @@ void main() {

Future<String> createContract(String wasmId) async {
await Future.delayed(Duration(seconds: 5));

// reload account for current sequence nr
AccountResponse submitter = await sdk.accounts.account(adminId);
Account? account = await sorobanServer.getAccount(adminId);
assert(account != null);
Account submitter = account!;

// build the operation for creating the contract
CreateContractHostFunction function =
Expand Down Expand Up @@ -180,9 +185,13 @@ void main() {
Future<void> createToken(
String contractId, String name, String symbol) async {
// see https://soroban.stellar.org/docs/reference/interfaces/token-interface
// reload account for sequence number

await Future.delayed(Duration(seconds: 5));
AccountResponse invoker = await sdk.accounts.account(adminId);

// reload account for sequence number
Account? account = await sorobanServer.getAccount(adminId);
assert(account != null);
Account invoker = account!;

Address adminAddress = Address.forAccountId(adminId);
String functionName = "initialize";
Expand Down Expand Up @@ -238,9 +247,13 @@ void main() {

Future<void> mint(String contractId, String toAccountId, int amount) async {
// see https://soroban.stellar.org/docs/reference/interfaces/token-interface
// reload account for sequence number

await Future.delayed(Duration(seconds: 5));
AccountResponse invoker = await sdk.accounts.account(adminId);

// reload account for sequence number
Account? account = await sorobanServer.getAccount(adminId);
assert(account != null);
Account invoker = account!;

Address toAddress = Address.forAccountId(toAccountId);
XdrSCVal amountVal = XdrSCVal.forI128(XdrInt128Parts.forHiLo(0, amount));
Expand Down Expand Up @@ -290,8 +303,11 @@ void main() {

Future<int> balance(String contractId, String accountId) async {
await Future.delayed(Duration(seconds: 5));

// reload account for sequence number
AccountResponse invoker = await sdk.accounts.account(adminId);
Account? account = await sorobanServer.getAccount(adminId);
assert(account != null);
Account invoker = account!;

Address address = Address.forAccountId(accountId);
String functionName = "balance";
Expand Down Expand Up @@ -342,8 +358,11 @@ void main() {

Future restoreContractFootprint(String contractCodePath) async {
await Future.delayed(Duration(seconds: 5));

// load account
AccountResponse accountA = await sdk.accounts.account(adminId);
Account? account = await sorobanServer.getAccount(adminId);
assert(account != null);
Account accountA = account!;

// load contract wasm file
Uint8List contractCode = await Util.readFile(contractCodePath);
Expand All @@ -370,7 +389,10 @@ void main() {
transactionData.resources.footprint.readOnly =
List<XdrLedgerKey>.empty(growable: false);

accountA = await sdk.accounts.account(adminId);
account = await sorobanServer.getAccount(adminId);
assert(account != null);
accountA = account!;

RestoreFootprintOperation restoreOp =
RestoreFootprintOperationBuilder().build();
transaction =
Expand Down Expand Up @@ -414,7 +436,9 @@ void main() {
await Future.delayed(Duration(seconds: 5));

// load account
AccountResponse accountA = await sdk.accounts.account(adminId);
Account? account = await sorobanServer.getAccount(adminId);
assert(account != null);
Account accountA = account!;

ExtendFootprintTTLOperation bumpFunction =
ExtendFootprintTTLOperationBuilder(extendTo).build();
Expand Down Expand Up @@ -445,7 +469,10 @@ void main() {
assert(simulateResponse.resultError == null);
assert(simulateResponse.transactionData != null);

accountA = await sdk.accounts.account(adminId);
account = await sorobanServer.getAccount(adminId);
assert(account != null);
accountA = account!;

// set transaction data, add resource fee and sign transaction
transaction.sorobanTransactionData = simulateResponse.transactionData;
transaction.addResourceFee(simulateResponse.minResourceFee!);
Expand Down Expand Up @@ -582,8 +609,9 @@ void main() {
];

// load submitter account for sequence number
AccountResponse swapSubmitter =
await sdk.accounts.account(swapSubmitterAccountId);
Account? account = await sorobanServer.getAccount(swapSubmitterAccountId);
assert(account != null);
Account swapSubmitter = account!;

InvokeContractHostFunction hostFunction = InvokeContractHostFunction(
atomicSwapContractId, swapFuntionName,
Expand Down
Loading

0 comments on commit 560ad5f

Please sign in to comment.