Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
7 changes: 6 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,8 @@ jobs:
with:
node-version: '16'
- uses: dtolnay/rust-toolchain@1.81.0
with:
target: wasm32-unknown-unknown
- uses: actions/download-artifact@v4.1.8
with:
name: solang-linux-x86-64
Expand All @@ -329,14 +331,17 @@ jobs:
echo "$(pwd)/bin" >> $GITHUB_PATH

- name: Install Soroban
run: cargo install --locked soroban-cli --version 22.0.0
run: cargo install --locked stellar-cli --version 22.0.0
- name: Add cargo install location to PATH
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- run: npm install
working-directory: ./integration/soroban
- name: Build Solang contracts
run: npm run build
working-directory: ./integration/soroban
- name: Build rust contracts
run: soroban contract build --profile release-with-logs
working-directory: ./integration/soroban/rust/contracts
- name: Setup Soroban enivronment
run: npm run setup
working-directory: ./integration/soroban
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ tests/create_me/

.helix/
.vscode/

/test_snapshots
6 changes: 5 additions & 1 deletion integration/soroban/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@
node_modules
package-lock.json
*.txt
*.toml
*.wasm
*.abi
target/
.stellar

6 changes: 6 additions & 0 deletions integration/soroban/callee.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
contract callee {
function add (uint64 a, uint64 b, uint64 c) public returns (uint64) {
print("add called in Solidity");
return a + b +c;
}
}
8 changes: 8 additions & 0 deletions integration/soroban/caller.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
contract caller {
function add (address addr, uint64 a, uint64 b, uint64 c) public returns (uint64) {
bytes payload = abi.encode("add", a, b, c);
(bool suc, bytes returndata) = addr.call(payload);
uint64 result = abi.decode(returndata, (uint64));
return result;
}
}
5 changes: 3 additions & 2 deletions integration/soroban/counter.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ describe('Counter', () => {
it('get correct initial counter', async () => {
// get the count
let count = await call_contract_function("count", server, keypair, contract);
expect(count.toString()).eq("10");
console.log(count.returnValue().value());
expect(count.returnValue().value().toString()).eq("10");
});

it('increment counter', async () => {
Expand All @@ -44,7 +45,7 @@ describe('Counter', () => {

// get the count
let count = await call_contract_function("count", server, keypair, contract);
expect(count.toString()).eq("11");
expect(count.returnValue().value().toString()).eq("11");
});
});

Expand Down
65 changes: 65 additions & 0 deletions integration/soroban/cross_contract.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import * as StellarSdk from '@stellar/stellar-sdk';
import { readFileSync } from 'fs';
import { expect } from 'chai';
import path from 'path';
import { fileURLToPath } from 'url';
import { call_contract_function, extractLogEvent } from './test_helpers.js';

const __filename = fileURLToPath(import.meta.url);
const dirname = path.dirname(__filename);
const server = new StellarSdk.SorobanRpc.Server("https://soroban-testnet.stellar.org:443");

function readContractAddress(filename) {
return readFileSync(path.join(dirname, '.soroban', 'contract-ids', filename), 'utf8').trim();
}

describe('Cross Contract Calls', () => {
let keypair, caller, callee, calleeRust;

before(async () => {
console.log('Setting up cross contract tests...');

keypair = StellarSdk.Keypair.fromSecret(readFileSync('alice.txt', 'utf8').trim());
caller = new StellarSdk.Contract(readContractAddress('caller.txt'));
callee = new StellarSdk.Contract(readContractAddress('callee.txt'));
calleeRust = new StellarSdk.Contract(readContractAddress('hello_world.txt'));
});

it('calls Rust contract', async () => {
let addr = calleeRust.address().toScVal();
let values = [
new StellarSdk.xdr.Uint64(BigInt(1)),
new StellarSdk.xdr.Uint64(BigInt(2)),
new StellarSdk.xdr.Uint64(BigInt(0))
].map(StellarSdk.xdr.ScVal.scvU64);

let res = await call_contract_function("add", server, keypair, caller, addr, ...values);
let returnValue = res.returnValue().value().toString();

console.log(returnValue);
expect(returnValue).to.equal("3");

let logMessages = extractLogEvent(res.diagnosticEvents()).logMessages;
console.log(logMessages);
expect(logMessages[0]).to.contain('Soroban SDK add function called!');
});

it('calls Solidity contract', async () => {
let addr = callee.address().toScVal();
let values = [
new StellarSdk.xdr.Uint64(BigInt(1)),
new StellarSdk.xdr.Uint64(BigInt(2)),
new StellarSdk.xdr.Uint64(BigInt(0))
].map(StellarSdk.xdr.ScVal.scvU64);

let res = await call_contract_function("add", server, keypair, caller, addr, ...values);
let returnValue = res.returnValue().value().toString();

console.log(returnValue);
expect(returnValue).to.equal("3");

let logMessages = extractLogEvent(res.diagnosticEvents()).logMessages;
console.log(logMessages);
expect(logMessages[0]).to.contain('add called in Solidity');
});
});
2 changes: 1 addition & 1 deletion integration/soroban/runtime_error.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ describe('Runtime Error', () => {
await call_contract_function("decrement", server, keypair, contract);
});

it('get correct initial counter', async () => {
it('prints error', async () => {

// decrement the counter again, resulting in a runtime error
let res = await call_contract_function("decrement", server, keypair, contract);
Expand Down
23 changes: 23 additions & 0 deletions integration/soroban/rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[workspace]
resolver = "2"
members = [
"contracts/*",
]

[workspace.dependencies]
soroban-sdk = "22"

[profile.release]
opt-level = "z"
overflow-checks = true
debug = 0
strip = "symbols"
debug-assertions = false
panic = "abort"
codegen-units = 1
lto = true

# For more information about this profile see https://soroban.stellar.org/docs/basic-tutorials/logging#cargotoml-profile
[profile.release-with-logs]
inherits = "release"
debug-assertions = true
15 changes: 15 additions & 0 deletions integration/soroban/rust/contracts/hello-world/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "hello-world"
version = "0.0.0"
edition = "2021"
publish = false

[lib]
crate-type = ["cdylib"]
doctest = false

[dependencies]
soroban-sdk = { workspace = true }

[dev-dependencies]
soroban-sdk = { workspace = true, features = ["testutils"] }
15 changes: 15 additions & 0 deletions integration/soroban/rust/contracts/hello-world/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: Apache-2.0

#![no_std]
use soroban_sdk::{contract, contractimpl, Env, log};

#[contract]
pub struct Contract;

#[contractimpl]
impl Contract {
pub fn add(env: Env, a: u64, b: u64, c: u64) -> u64 {
log!(&env,"Soroban SDK add function called!");
a + b + c
}
}
10 changes: 8 additions & 2 deletions integration/soroban/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ function exe(command) {
}

function generate_alice() {
exe(`${soroban} keys generate alice --network testnet`);
exe(`${soroban} keys generate alice --network testnet --overwrite`);

// get the secret key of alice and put it in alice.txt
exe(`${soroban} keys show alice > alice.txt`);
Expand All @@ -43,7 +43,13 @@ function deploy_all() {
const contractsDir = path.join(dirname, '.soroban', 'contract-ids');
mkdirSync(contractsDir, { recursive: true });

const wasmFiles = readdirSync(`${dirname}`).filter(file => file.endsWith('.wasm'));
let wasmFiles = readdirSync(`${dirname}`).filter(file => file.endsWith('.wasm'));
console.log(dirname);

let rust_wasm = path.join('rust','target','wasm32-unknown-unknown', 'release-with-logs', 'hello_world.wasm');

// add rust wasm file to the list of wasm files
wasmFiles.push(rust_wasm);

wasmFiles.forEach(wasmFile => {
deploy(path.join(dirname, wasmFile));
Expand Down
36 changes: 24 additions & 12 deletions integration/soroban/storage_types.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,20 @@ describe('StorageTypes', () => {

it('check initial values', async () => {
// Check initial values of all storage variables
let sesa = await call_contract_function("sesa", server, keypair, contract);
let res = await call_contract_function("sesa", server, keypair, contract);
let sesa = res.returnValue().value();
expect(sesa.toString()).eq("1");

let sesa1 = await call_contract_function("sesa1", server, keypair, contract);
res = await call_contract_function("sesa1", server, keypair, contract);
let sesa1 = res.returnValue().value();
expect(sesa1.toString()).eq("1");

let sesa2 = await call_contract_function("sesa2", server, keypair, contract);
res = await call_contract_function("sesa2", server, keypair, contract);
let sesa2 = res.returnValue().value();
expect(sesa2.toString()).eq("2");

let sesa3 = await call_contract_function("sesa3", server, keypair, contract);
res = await call_contract_function("sesa3", server, keypair, contract);
let sesa3 = res.returnValue().value();
expect(sesa3.toString()).eq("2");
});

Expand All @@ -51,16 +55,20 @@ describe('StorageTypes', () => {
await call_contract_function("inc", server, keypair, contract);

// Check the incremented values
let sesa = await call_contract_function("sesa", server, keypair, contract);
let res = await call_contract_function("sesa", server, keypair, contract);
let sesa = res.returnValue().value();
expect(sesa.toString()).eq("2");

let sesa1 = await call_contract_function("sesa1", server, keypair, contract);
res = await call_contract_function("sesa1", server, keypair, contract);
let sesa1 = res.returnValue().value();
expect(sesa1.toString()).eq("2");

let sesa2 = await call_contract_function("sesa2", server, keypair, contract);
res = await call_contract_function("sesa2", server, keypair, contract);
let sesa2 = res.returnValue().value();
expect(sesa2.toString()).eq("3");

let sesa3 = await call_contract_function("sesa3", server, keypair, contract);
res = await call_contract_function("sesa3", server, keypair, contract);
let sesa3 = res.returnValue().value();
expect(sesa3.toString()).eq("3");
});

Expand All @@ -69,16 +77,20 @@ describe('StorageTypes', () => {
await call_contract_function("dec", server, keypair, contract);

// Check the decremented values
let sesa = await call_contract_function("sesa", server, keypair, contract);
let res = await call_contract_function("sesa", server, keypair, contract);
let sesa = res.returnValue().value();
expect(sesa.toString()).eq("1");

let sesa1 = await call_contract_function("sesa1", server, keypair, contract);
res = await call_contract_function("sesa1", server, keypair, contract);
let sesa1 = res.returnValue().value();
expect(sesa1.toString()).eq("1");

let sesa2 = await call_contract_function("sesa2", server, keypair, contract);
res = await call_contract_function("sesa2", server, keypair, contract);
let sesa2 = res.returnValue().value();
expect(sesa2.toString()).eq("2");

let sesa3 = await call_contract_function("sesa3", server, keypair, contract);
res = await call_contract_function("sesa3", server, keypair, contract);
let sesa3 = res.returnValue().value();
expect(sesa3.toString()).eq("2");
});
});
28 changes: 23 additions & 5 deletions integration/soroban/test_helpers.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import * as StellarSdk from '@stellar/stellar-sdk';

export async function call_contract_function(method, server, keypair, contract) {
export async function call_contract_function(method, server, keypair, contract, ... params) {
let res = null;

try {
let builtTransaction = new StellarSdk.TransactionBuilder(await server.getAccount(keypair.publicKey()), {
fee: StellarSdk.BASE_FEE,
networkPassphrase: StellarSdk.Networks.TESTNET,
}).addOperation(contract.call(method)).setTimeout(30).build();
}).addOperation(contract.call(method, ...params)).setTimeout(30).build();

let preparedTransaction = await server.prepareTransaction(builtTransaction);

Expand All @@ -34,9 +34,8 @@ export async function call_contract_function(method, server, keypair, contract)
}
// Extract and return the return value from the contract
let transactionMeta = getResponse.resultMetaXdr;
let returnValue = transactionMeta.v3().sorobanMeta().returnValue();
console.log(`Transaction result: ${returnValue.value()}`);
res = returnValue.value();
let returnValue = transactionMeta.v3().sorobanMeta();
res = returnValue;
} else {
throw `Transaction failed: ${getResponse.resultXdr}`;
}
Expand All @@ -62,3 +61,22 @@ export async function call_contract_function(method, server, keypair, contract)

return res;
}

export function extractLogEvent(diagnosticEvents) {
// Convert events into human-readable format
const humanReadableEvents = StellarSdk.humanizeEvents(diagnosticEvents);

// Find the log event
const logEvent = humanReadableEvents.find(event =>
event.type === "diagnostic" && event.topics.includes("log")
);

if (logEvent) {
return {
contractId: logEvent.contractId || "Unknown Contract",
logMessages: Array.isArray(logEvent.data) ? logEvent.data : [logEvent.data]
};
}

return null; // No log event found
}
Loading