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

feat: Generate TS from XDR #871

Merged
merged 70 commits into from
Jun 12, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
7b082e8
feat: initial work to generate TS from XDR
willemneal Feb 21, 2023
d3a5cee
feat: add method to generate TS project
willemneal May 18, 2023
21571ae
fix: move assert_fs to dev-deps
willemneal May 18, 2023
41dc7a9
fix: make public
willemneal May 18, 2023
d3b39be
fix: make public
willemneal May 18, 2023
a23320f
fix: type names
willemneal May 18, 2023
9239428
feat: add option and result support
willemneal May 20, 2023
b479773
fix: don't include args if there are none
willemneal May 22, 2023
f9e478f
chore: refactor _Error type to interface; rename inner field to message
willemneal May 22, 2023
53e469d
chore: add gitignore
willemneal May 22, 2023
273ef99
fix: move buffer conversion inside caller
willemneal May 22, 2023
733e312
fix: temporarily skip unions
willemneal May 22, 2023
96c0aec
fix: update Result type implementation
willemneal May 22, 2023
315ebf5
fix: add `version` to template package.json
chadoh May 23, 2023
28dfad8
feat: add enum support
willemneal May 23, 2023
5b3cb03
feat: better constant injection
chadoh May 23, 2023
096a3bf
Apply suggestions from code review
willemneal May 23, 2023
74d6983
chore: clean up template variable injection
chadoh May 24, 2023
fb6fd91
chore: rm accidental dir
chadoh May 24, 2023
8ad59e8
chore: update package-lock
chadoh May 24, 2023
13e4c1b
fix: remove 'window' assumption
chadoh May 24, 2023
040efc2
feat: no longer require 'sign' argument
chadoh May 24, 2023
b91f57e
feat: guard against unlikely auth edge cases
chadoh May 24, 2023
240d35f
clean up with Willem's suggestion
chadoh May 24, 2023
779192d
build: buggily upgrade to [email protected]
chadoh May 25, 2023
cf6e60d
feat: update ts
willemneal May 25, 2023
379d818
build: update to latest js-soroban-client version
chadoh May 31, 2023
d718324
feat: make network settings configurable
chadoh Jun 1, 2023
b983d06
fix: don't need network name
chadoh Jun 1, 2023
73468b0
fix: generate actual ScVec
willemneal Jun 2, 2023
2447aad
feat: update xdr to js to allow for arrays, maps, strings, and symbols
willemneal Jun 2, 2023
bdd5230
feat: add proper result support. Now result types return Ok or Err
willemneal Jun 2, 2023
7398c9a
fix: remove signme
willemneal Jun 2, 2023
18e78b6
fix: clippy
willemneal Jun 2, 2023
0d0523f
feat: add proper project generation test
willemneal Jun 2, 2023
320ff4d
Merge branch 'release/v0.8.5' into feat/gen_ts
willemneal Jun 2, 2023
1c55528
feat: add rest of main types
willemneal Jun 5, 2023
30296b1
fix: ensure string as input
willemneal Jun 5, 2023
7c3e3cf
Apply suggestions from code review
willemneal Jun 6, 2023
ea86855
fix: remove unused functions and address nit
willemneal Jun 6, 2023
dac4331
chore: ts -> typescript
willemneal Jun 6, 2023
48e6415
feat: generate `Promise<void>` when necessary
willemneal Jun 6, 2023
b70ac5c
chore: use `dist/` instead of `out/`
willemneal Jun 6, 2023
f3e4edb
feat: use bigint-conversion library to not reinvent it
willemneal Jun 6, 2023
c4cf6ac
chore: update invoke docs and rebuild snapshot
willemneal Jun 6, 2023
31e7709
fix: convert.ts errors/warnings when building projects
chadoh Jun 6, 2023
0bbe3fa
feat: simplify & expose `signAndSendTx`
chadoh Jun 6, 2023
275a8c9
Apply suggestions from code review
willemneal Jun 6, 2023
db7e97b
fix: update snapshot
willemneal Jun 6, 2023
b08af68
feat: break signAndSendTx into two
chadoh Jun 6, 2023
6da9299
fix: snapshot
willemneal Jun 6, 2023
8782cbe
chore: add more details to contributing doc
willemneal Jun 6, 2023
502b4e7
fix: remove extra whitespace
willemneal Jun 6, 2023
41b4c43
fix: use tempdir
willemneal Jun 7, 2023
a9d7869
fix: use temp-dir to avoid another deny issue
willemneal Jun 8, 2023
4d8dee5
build: target commonjs
chadoh Jun 8, 2023
de9ac82
Revert "build: target commonjs"
chadoh Jun 8, 2023
10b9c81
feat: add signAndSend option to each contract method
willemneal Jun 9, 2023
16af16e
feat: switch on signAndSend
willemneal Jun 9, 2023
6b99032
build: make esm & cjs; workaround nextjs limitations
chadoh Jun 9, 2023
b56c1c9
fix: don't add comma if there are not inputs for a contract method
willemneal Jun 9, 2023
516638a
fix: default options for contract functions
chadoh Jun 9, 2023
c692e8f
fix: treat all top level maps as maps
willemneal Jun 12, 2023
01098b4
fix: rm obsolete env.d.ts; export CONTRACT_ID_HEX
chadoh Jun 12, 2023
cae5a4e
fix: convert non-hex contract addresses
willemneal Jun 12, 2023
6228826
fix: addressToScVal works, but the wrong way
chadoh Jun 12, 2023
969b1dc
fix: upgrade soroban-client; rm gibberish
chadoh Jun 12, 2023
febd70a
fix: suggestions
willemneal Jun 12, 2023
12e11b1
Apply suggestions from code review
willemneal Jun 12, 2023
93f9234
fix: make each property in the options arg optional
willemneal Jun 12, 2023
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
126 changes: 53 additions & 73 deletions soroban-spec/src/gen/ts/boilerplate.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#![allow(non_snake_case)]
use heck::ToLowerCamelCase;
willemneal marked this conversation as resolved.
Show resolved Hide resolved
use heck::ToShoutySnakeCase;
willemneal marked this conversation as resolved.
Show resolved Hide resolved
use include_dir::{include_dir, Dir};
use serde::Serialize;
use std::{
fs,
io::Write,
Expand Down Expand Up @@ -37,86 +37,66 @@ impl Project {
contract_id: &str,
spec: &[ScSpecEntry],
) -> std::io::Result<()> {
self.add_contract_id(contract_name, contract_id)?;
self.add_interface(contract_name)?;
self.update_package_json(contract_name)?;
self.replace_placeholder_patterns(contract_name, contract_id)?;
self.append_index_ts(spec)
}

fn append_index_ts(&self, spec: &[ScSpecEntry]) -> std::io::Result<()> {
fs::OpenOptions::new()
.append(true)
.open(self.0.join("src/index.ts"))?
.write_all(generate(spec).as_bytes())
}

fn add_contract_id(&self, contract_name: &str, contract_id: &str) -> std::io::Result<()> {
fn replace_placeholder_patterns(
&self,
contract_name: &str,
contract_id: &str,
) -> std::io::Result<()> {
let replacement_strings = &[
("INSERT_CONTRACT_NAME_HERE", contract_name.to_string()),
(
"INSERT_SCREAMING_SNAKE_CASE_CONTRACT_NAME_HERE",
contract_name.to_shouty_snake_case(),
),
(
"INSERT_CAMEL_CASE_CONTRACT_NAME_HERE",
contract_name.to_lower_camel_case(),
),
("INSERT_CONTRACT_ID_HERE", contract_id.to_string()),
("INSERT_NETWORK_NAME_HERE", "FUTURENET".to_string()),
(
"INSERT_NETWORK_PASSPHRASE_HERE",
"Test SDF Future Network ; October 2022".to_string(),
),
(
"INSERT_RPC_URL_HERE",
"https://rpc-futurenet.stellar.org:443/soroban/rpc".to_string(),
),
];
let root: &Path = self.as_ref();
let CONTRACT_NAME = contract_name.to_shouty_snake_case();
fs::OpenOptions::new()
.append(true)
.open(root.join("src/constants.ts"))?
.write_all(
format!(
r#"
/**
* The Soroban contract ID for the `{contract_name}` contract.
*
* You can override this by setting a `SOROBAN_{CONTRACT_NAME}_CONTRACT_ID` or
* `PUBLIC_SOROBAN_{CONTRACT_NAME}_CONTRACT_ID` environment variable.
*/
export const CONTRACT_ID = import.meta.env.PUBLIC_SOROBAN_{CONTRACT_NAME}_CONTRACT_ID
?? import.meta.env.SOROBAN_{CONTRACT_NAME}_CONTRACT_ID
?? '{contract_id}'
"#
)
.as_bytes(),
)
[
"package.json",
"README.md",
"src/constants.ts",
"src/convert.ts",
"src/env.d.ts",
"src/index.ts",
"src/invoke.ts",
"src/server.ts",
]
.into_iter()
.map(|file_name| {
let file = &root.join(file_name);
let mut contents = fs::read_to_string(file).unwrap();
replacement_strings
.iter()
.for_each(|(pattern, replacement)| {
contents = contents.replace(pattern, replacement);
});
fs::write(file, contents)
})
.collect::<std::io::Result<()>>()
}

fn add_interface(&self, contract_name: &str) -> std::io::Result<()> {
let CONTRACT_NAME = contract_name.to_shouty_snake_case();
let root: &Path = self.as_ref();

fn append_index_ts(&self, spec: &[ScSpecEntry]) -> std::io::Result<()> {
fs::OpenOptions::new()
.append(true)
.open(root.join("src/env.d.ts"))?
.write_all(
format!(
r#"
interface ImportMetaEnv {{
readonly PUBLIC_SOROBAN_{CONTRACT_NAME}_CONTRACT_ID: string;
readonly SOROBAN_{CONTRACT_NAME}_CONTRACT_ID: string;

readonly PUBLIC_SOROBAN_NETWORK_NAME: string;
readonly SOROBAN_NETWORK_NAME: string;

readonly PUBLIC_SOROBAN_NETWORK_PASSPHRASE: string;
readonly SOROBAN_NETWORK_PASSPHRASE: string;

readonly PUBLIC_SOROBAN_RPC_URL: string;
readonly SOROBAN_RPC_URL: string;
}}
"#
)
.as_bytes(),
)
}

fn update_package_json(&self, contract_name: &str) -> std::io::Result<()> {
let p = self.0.join("package.json");
let mut value: serde_json::Value = fs::read_to_string(&p)?.parse()?;
let obj = value.as_object_mut().unwrap();
obj.insert(
"name".to_owned(),
serde_json::Value::String(contract_name.to_owned()),
);
let mut buf = Vec::new();
let formatter = serde_json::ser::PrettyFormatter::with_indent(b" ");
let mut ser = serde_json::Serializer::with_formatter(&mut buf, formatter);
obj.serialize(&mut ser).unwrap();
fs::write(&p, &buf)?;
Ok(())
.open(self.0.join("src/index.ts"))?
.write_all(generate(spec).as_bytes())
}
}

Expand Down
28 changes: 14 additions & 14 deletions soroban-spec/src/gen/ts/project_template/README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
# abundance-token JS
# INSERT_CONTRACT_NAME_HERE JS

JS library for interacting with [Soroban](https://soroban.stellar.org/) smart contract `abundance-token` via Soroban RPC.
JS library for interacting with [Soroban](https://soroban.stellar.org/) smart contract `INSERT_CONTRACT_NAME_HERE` via Soroban RPC.

This library was automatically generated by Soroban CLI using a command similar to:

```bash
soroban contract bindings ts \
--rpc-url https://rpc-futurenet.stellar.org:443/soroban/rpc \
--network-passphrase "Test SDF Future Network ; October 2022" \
--id 2c6c3b8ba9923d029d8ef7eb80080384b1da32bcf0698290119fdfbf3f2a01de \
--name abundance-token
--rpc-url INSERT_RPC_URL_HERE \
--network-passphrase "INSERT_NETWORK_PASSPHRASE_HERE" \
--id INSERT_CONTRACT_ID_HERE \
--name INSERT_CONTRACT_NAME_HERE
```

It uses these settings by default, but you can override them with environment variables if you need to:

- **Contract ID**: `2c6c3b8ba9923d029d8ef7eb80080384b1da32bcf0698290119fdfbf3f2a01de`
- **Contract ID**: `INSERT_CONTRACT_ID_HERE`

Override with environment variable `SOROBAN_ABUNDANCE_TOKEN_CONTRACT_ID` or `PUBLIC_SOROBAN_ABUNDANCE_TOKEN_CONTRACT_ID`
Override with environment variable `SOROBAN_INSERT_SCREAMING_SNAKE_CASE_CONTRACT_NAME_HERE_CONTRACT_ID` or `PUBLIC_SOROBAN_INSERT_SCREAMING_SNAKE_CASE_CONTRACT_NAME_HERE_CONTRACT_ID`

- **RPC endpoint**: `https://rpc-futurenet.stellar.org:443/soroban/rpc`
- **RPC endpoint**: `INSERT_RPC_URL_HERE`

Override with environment variable `SOROBAN_RPC_URL` or `PUBLIC_SOROBAN_RPC_URL`

- **Network Passphrase**: `Test SDF Future Network ; October 2022`
- **Network Passphrase**: `INSERT_NETWORK_PASSPHRASE_HERE`

Override with environment variable `SOROBAN_NETWORK_PASSPHRASE` or `PUBLIC_SOROBAN_NETWORK_PASSPHRASE`

- **Freighter Network Name**: `FUTURENET`
- **Freighter Network Name**: `INSERT_NETWORK_NAME_HERE`

What's this? [Freighter](https://www.freighter.app/) is the only signer/wallet supported for now. The [`signTransaction` method](https://docs.freighter.app/docs/guide/usingfreighternode/#signtransaction) requires a [specific naming convention](https://github.com/stellar/freighter/blob/7158e5f3b0bcb6b8d9086312955d1ce94352410e/%40shared/constants/stellar.ts#L12) for different networks.

Expand All @@ -39,17 +39,17 @@ You don't need to publish this library to NPM to use it. You can add it to your
```json
{
"dependencies": {
"abundance-token": "./path/to/this/folder"
"INSERT_CONTRACT_NAME_HERE": "./path/to/this/folder"
}
}
```

Then you can import it into your editor and see inline documentation for all of its exports:

```js
import * as abundanceToken from "abundance-token"
import * as INSERT_CAMEL_CASE_CONTRACT_NAME_HERE from "INSERT_CONTRACT_NAME_HERE"

abundanceToken.|
INSERT_CAMEL_CASE_CONTRACT_NAME_HERE.|
```

As long as your editor is configured to show JavaScript/TypeScript documentation, you can pause your typing at that `|` to get a list of all exports and inline-documentation for each. It exports a separate [async](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) function for each method in the smart contract, with documentation for each generated from the comments the contract's author included in the original source code.
2 changes: 2 additions & 0 deletions soroban-spec/src/gen/ts/project_template/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion soroban-spec/src/gen/ts/project_template/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"version": "0.0.0",
"name": "abundance-token",
"name": "INSERT_CONTRACT_NAME_HERE",
"dependencies": {
"@stellar/freighter-api": "1.4.0",
"buffer": "6.0.3",
Expand Down
16 changes: 13 additions & 3 deletions soroban-spec/src/gen/ts/project_template/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
/**
* The Soroban contract ID for the `INSERT_CONTRACT_NAME_HERE` contract.
*
* You can override this by setting a `SOROBAN_INSERT_SCREAMING_SNAKE_CASE_CONTRACT_NAME_HERE_CONTRACT_ID` or
* `PUBLIC_SOROBAN_INSERT_SCREAMING_SNAKE_CASE_CONTRACT_NAME_HERE_CONTRACT_ID` environment variable.
*/
export const CONTRACT_ID = import.meta.env.PUBLIC_SOROBAN_INSERT_SCREAMING_SNAKE_CASE_CONTRACT_NAME_HERE_CONTRACT_ID
?? import.meta.env.SOROBAN_INSERT_SCREAMING_SNAKE_CASE_CONTRACT_NAME_HERE_CONTRACT_ID
?? 'INSERT_CONTRACT_ID_HERE'

/**
* Matches the name given by Freighter's `getNetworkDetails().network` for the network used to initialize this library.
*
Expand All @@ -6,7 +16,7 @@
*/
export const NETWORK_NAME = import.meta.env.PUBLIC_SOROBAN_NETWORK_NAME
?? import.meta.env.SOROBAN_NETWORK_NAME
?? 'FUTURENET'
?? 'INSERT_NETWORK_NAME_HERE'

/**
* The Soroban network passphrase used to initialize this library.
Expand All @@ -16,7 +26,7 @@ export const NETWORK_NAME = import.meta.env.PUBLIC_SOROBAN_NETWORK_NAME
*/
export const NETWORK_PASSPHRASE = import.meta.env.PUBLIC_SOROBAN_NETWORK_PASSPHRASE
?? import.meta.env.SOROBAN_NETWORK_PASSPHRASE
?? 'Test SDF Future Network ; October 2022'
?? 'INSERT_NETWORK_PASSPHRASE_HERE'

/**
* The Soroban RPC endpoint used to initialize this library.
Expand All @@ -26,5 +36,5 @@ export const NETWORK_PASSPHRASE = import.meta.env.PUBLIC_SOROBAN_NETWORK_PASSPHR
*/
export const RPC_URL = import.meta.env.PUBLIC_SOROBAN_RPC_URL
?? import.meta.env.SOROBAN_RPC_URL
?? 'https://rpc-futurenet.stellar.org:443/soroban/rpc'
?? 'INSERT_RPC_URL_HERE'

12 changes: 12 additions & 0 deletions soroban-spec/src/gen/ts/project_template/src/env.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
interface ImportMetaEnv {
readonly PUBLIC_SOROBAN_INSERT_SCREAMING_SNAKE_CASE_CONTRACT_NAME_HERE_CONTRACT_ID: string;
readonly SOROBAN_INSERT_SCREAMING_SNAKE_CASE_CONTRACT_NAME_HERE_CONTRACT_ID: string;

readonly PUBLIC_SOROBAN_NETWORK_NAME: string;
readonly SOROBAN_NETWORK_NAME: string;

readonly PUBLIC_SOROBAN_NETWORK_PASSPHRASE: string;
readonly SOROBAN_NETWORK_PASSPHRASE: string;

readonly PUBLIC_SOROBAN_RPC_URL: string;
readonly SOROBAN_RPC_URL: string;
}

interface ImportMeta {
readonly env: ImportMetaEnv;
Expand Down
6 changes: 4 additions & 2 deletions soroban-spec/src/gen/ts/project_template/src/invoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ export async function getAccount(): Promise<Account> {
}

/**
* Invoke a method on the Abundance token contract.
* Invoke a method on the INSERT_CONTRACT_NAME_HERE contract.
*
* Uses Freighter to determine the current user and sign the transaction.
*
* @param {string} obj.method - The method to invoke.
* @param {any[]} obj.args - The arguments to pass to the method.
Expand Down Expand Up @@ -121,4 +123,4 @@ async function invokeRpc(tx: Tx): Promise<TxResponse> {
}

return getTransactionResponse
}
}