Skip to content

Commit 2536c08

Browse files
Support different Soroban storage types (#1664)
1 parent de4fc34 commit 2536c08

File tree

41 files changed

+949
-49
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+949
-49
lines changed

fmt/src/formatter.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::{
2020
};
2121
use alloy_primitives::Address;
2222
use itertools::{Either, Itertools};
23-
use solang_parser::pt::{PragmaDirective, VersionComparator};
23+
use solang_parser::pt::{PragmaDirective, StorageType, VersionComparator};
2424
use std::{fmt::Write, str::FromStr};
2525
use thiserror::Error;
2626

@@ -3333,6 +3333,11 @@ impl<'a, W: Write> Visitor for Formatter<'a, W> {
33333333
self.visit_list("", idents, Some(loc.start()), Some(loc.end()), false)?;
33343334
None
33353335
}
3336+
VariableAttribute::StorageType(s) => match s {
3337+
StorageType::Instance(_) => Some("instance".to_string()),
3338+
StorageType::Temporary(_) => Some("temporary".to_string()),
3339+
StorageType::Persistent(_) => Some("persistent".to_string()),
3340+
},
33363341
};
33373342
if let Some(token) = token {
33383343
let loc = attribute.loc();

fmt/src/solang_ext/ast_eq.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,17 @@ impl AstEq for VariableDefinition {
7575
}
7676
}
7777

78+
impl AstEq for StorageType {
79+
fn ast_eq(&self, other: &Self) -> bool {
80+
matches!(
81+
(self, other),
82+
(StorageType::Instance(_), StorageType::Instance(_))
83+
| (StorageType::Persistent(_), StorageType::Persistent(_))
84+
| (StorageType::Temporary(_), StorageType::Temporary(_))
85+
)
86+
}
87+
}
88+
7889
impl AstEq for FunctionDefinition {
7990
fn ast_eq(&self, other: &Self) -> bool {
8091
// attributes
@@ -726,5 +737,6 @@ derive_ast_eq! { enum VariableAttribute {
726737
Constant(loc),
727738
Immutable(loc),
728739
Override(loc, idents),
740+
StorageType(s)
729741
_
730742
}}

integration/polkadot/try_catch.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ contract TryCatchCaller {
22
constructor() payable {}
33

44
function test(uint128 div) public payable returns (uint128) {
5-
TryCatchCallee instance = new TryCatchCallee();
5+
TryCatchCallee contract_instance = new TryCatchCallee();
66

7-
try instance.test(div) returns (uint128) {
7+
try contract_instance.test(div) returns (uint128) {
88
return 4;
99
} catch Error(string reason) {
1010
assert(reason == "foo");

integration/soroban/.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
*.js
21
*.so
32
*.key
43
*.json

integration/soroban/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
"mocha": "^10.4.0"
88
},
99
"scripts": {
10-
"build": "solang compile *.sol --target soroban",
10+
"build": "solang compile *.sol --target soroban && solang compile storage_types.sol --target soroban --release",
1111
"setup": "node setup.js",
12-
"test": "mocha *.spec.js --timeout 20000"
12+
"test": "mocha *.spec.js --timeout 100000"
1313
},
1414
"devDependencies": {
1515
"@eslint/js": "^9.4.0",
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import * as StellarSdk from '@stellar/stellar-sdk';
2+
import { readFileSync } from 'fs';
3+
import { expect } from 'chai';
4+
import path from 'path';
5+
import { fileURLToPath } from 'url';
6+
import { call_contract_function } from './test_helpers.js';
7+
8+
const __filename = fileURLToPath(import.meta.url);
9+
const dirname = path.dirname(__filename);
10+
11+
describe('Runtime Error', () => {
12+
let keypair;
13+
const server = new StellarSdk.SorobanRpc.Server(
14+
"https://soroban-testnet.stellar.org:443",
15+
);
16+
17+
let contractAddr;
18+
let contract;
19+
before(async () => {
20+
21+
console.log('Setting up runtime_error.sol contract tests...');
22+
23+
// read secret from file
24+
const secret = readFileSync('alice.txt', 'utf8').trim();
25+
keypair = StellarSdk.Keypair.fromSecret(secret);
26+
27+
let contractIdFile = path.join(dirname, '.soroban', 'contract-ids', 'Error.txt');
28+
// read contract address from file
29+
contractAddr = readFileSync(contractIdFile, 'utf8').trim().toString();
30+
31+
// load contract
32+
contract = new StellarSdk.Contract(contractAddr);
33+
34+
// call decrement once. The second call however will result in a runtime error
35+
await call_contract_function("decrement", server, keypair, contract);
36+
});
37+
38+
it('get correct initial counter', async () => {
39+
40+
// decrement the counter again, resulting in a runtime error
41+
let res = await call_contract_function("decrement", server, keypair, contract);
42+
43+
expect(res).to.contain('runtime_error: math overflow in runtime_error.sol:6:9-19');
44+
});
45+
46+
});
47+
48+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
contract storage_types {
2+
3+
uint64 public temporary sesa = 1;
4+
uint64 public instance sesa1 = 1;
5+
uint64 public persistent sesa2 = 2;
6+
uint64 public sesa3 = 2;
7+
8+
function inc() public {
9+
sesa++;
10+
sesa1++;
11+
sesa2++;
12+
sesa3++;
13+
}
14+
15+
function dec() public {
16+
sesa--;
17+
sesa1--;
18+
sesa2--;
19+
sesa3--;
20+
}
21+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import * as StellarSdk from '@stellar/stellar-sdk';
2+
import { readFileSync } from 'fs';
3+
import { expect } from 'chai';
4+
import path from 'path';
5+
import { fileURLToPath } from 'url';
6+
import { call_contract_function } from './test_helpers.js'; // Helper to interact with the contract
7+
8+
const __filename = fileURLToPath(import.meta.url);
9+
const dirname = path.dirname(__filename);
10+
11+
describe('StorageTypes', () => {
12+
let keypair;
13+
const server = new StellarSdk.SorobanRpc.Server(
14+
"https://soroban-testnet.stellar.org:443",
15+
);
16+
17+
let contractAddr;
18+
let contract;
19+
before(async () => {
20+
console.log('Setting up storage_types contract tests...');
21+
22+
// Read secret from file
23+
const secret = readFileSync('alice.txt', 'utf8').trim();
24+
keypair = StellarSdk.Keypair.fromSecret(secret);
25+
26+
let contractIdFile = path.join(dirname, '.soroban', 'contract-ids', 'storage_types.txt');
27+
// Read contract address from file
28+
contractAddr = readFileSync(contractIdFile, 'utf8').trim().toString();
29+
30+
// Load contract
31+
contract = new StellarSdk.Contract(contractAddr);
32+
});
33+
34+
it('check initial values', async () => {
35+
// Check initial values of all storage variables
36+
let sesa = await call_contract_function("sesa", server, keypair, contract);
37+
expect(sesa.toString()).eq("1");
38+
39+
let sesa1 = await call_contract_function("sesa1", server, keypair, contract);
40+
expect(sesa1.toString()).eq("1");
41+
42+
let sesa2 = await call_contract_function("sesa2", server, keypair, contract);
43+
expect(sesa2.toString()).eq("2");
44+
45+
let sesa3 = await call_contract_function("sesa3", server, keypair, contract);
46+
expect(sesa3.toString()).eq("2");
47+
});
48+
49+
it('increment values', async () => {
50+
// Increment all values by calling the inc function
51+
await call_contract_function("inc", server, keypair, contract);
52+
53+
// Check the incremented values
54+
let sesa = await call_contract_function("sesa", server, keypair, contract);
55+
expect(sesa.toString()).eq("2");
56+
57+
let sesa1 = await call_contract_function("sesa1", server, keypair, contract);
58+
expect(sesa1.toString()).eq("2");
59+
60+
let sesa2 = await call_contract_function("sesa2", server, keypair, contract);
61+
expect(sesa2.toString()).eq("3");
62+
63+
let sesa3 = await call_contract_function("sesa3", server, keypair, contract);
64+
expect(sesa3.toString()).eq("3");
65+
});
66+
67+
it('decrement values', async () => {
68+
// Decrement all values by calling the dec function
69+
await call_contract_function("dec", server, keypair, contract);
70+
71+
// Check the decremented values
72+
let sesa = await call_contract_function("sesa", server, keypair, contract);
73+
expect(sesa.toString()).eq("1");
74+
75+
let sesa1 = await call_contract_function("sesa1", server, keypair, contract);
76+
expect(sesa1.toString()).eq("1");
77+
78+
let sesa2 = await call_contract_function("sesa2", server, keypair, contract);
79+
expect(sesa2.toString()).eq("2");
80+
81+
let sesa3 = await call_contract_function("sesa3", server, keypair, contract);
82+
expect(sesa3.toString()).eq("2");
83+
});
84+
});

solang-parser/src/helpers/fmt.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//!
55
//! [ref]: https://docs.soliditylang.org/en/latest/style-guide.html
66
7-
use crate::pt;
7+
use crate::pt::{self, StorageType};
88
use std::{
99
borrow::Cow,
1010
fmt::{Display, Formatter, Result, Write},
@@ -1169,6 +1169,11 @@ impl Display for pt::VariableAttribute {
11691169
}
11701170
Ok(())
11711171
}
1172+
Self::StorageType(storage) => match storage {
1173+
StorageType::Instance(_) => f.write_str("instance"),
1174+
StorageType::Temporary(_) => f.write_str("temporary"),
1175+
StorageType::Persistent(_) => f.write_str("persistent"),
1176+
},
11721177
}
11731178
}
11741179
}

solang-parser/src/helpers/loc.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ impl OptionalCodeLocation for pt::Visibility {
2828
}
2929
}
3030

31+
impl OptionalCodeLocation for pt::StorageType {
32+
fn loc_opt(&self) -> Option<Loc> {
33+
match self {
34+
Self::Persistent(l) | Self::Temporary(l) | Self::Instance(l) => *l,
35+
}
36+
}
37+
}
38+
3139
impl OptionalCodeLocation for pt::SourceUnit {
3240
#[inline]
3341
fn loc_opt(&self) -> Option<Loc> {
@@ -431,6 +439,7 @@ impl_for_enums! {
431439

432440
pt::VariableAttribute: match self {
433441
Self::Visibility(ref l, ..) => l.loc_opt().unwrap_or_default(),
442+
Self::StorageType(ref l, ..) => l.loc_opt().unwrap_or_default(),
434443
Self::Constant(l, ..)
435444
| Self::Immutable(l, ..)
436445
| Self::Override(l, ..) => l,

0 commit comments

Comments
 (0)