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

docs(compiler): explain compilerPath argument and related fixes #1870

Merged
merged 6 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion docs/guides/address-length.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,5 @@ Running the above code you would get output like
Therefore the minimum address length is 41 chars. All these addresses valid, for example
`ak_11111111111111111111111111111111273Yts` [used] to collect AENS name fees.

[isAddressValid]: https://docs.aeternity.com/aepp-sdk-js/v13.1.0/api/functions/isAddressValid.html
[isAddressValid]: https://docs.aeternity.com/aepp-sdk-js/v13.2.1/api/functions/isAddressValid.html
[used]: https://mainnet.aeternity.io/v3/accounts/ak_11111111111111111111111111111111273Yts
82 changes: 47 additions & 35 deletions docs/guides/contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,33 @@ The smart contract language of the æternity blockchain is [Sophia](https://docs

Before interacting with contracts using the SDK you should get familiar with Sophia itself first. Have a look into [aepp-sophia-examples](https://github.com/aeternity/aepp-sophia-examples) and start rapid prototyping using [AEstudio](https://studio.aepps.com).

The SDK needs to interact with following components in order to enable smart contract interactions on the æternity blockchain:

- [æternity](https://github.com/aeternity/aeternity) (host your own one or use the public testnet node at `https://testnet.aeternity.io`)
- [aesophia_http](https://github.com/aeternity/aesophia_http) (host your own one or use the public compiler at `https://v7.compiler.aepps.com`)

Note:

- For production deployments you should ***always*** host these services by yourself.

## 1. Specify imports
```js
// node.js import
const { AeSdk, MemoryAccount, Node, CompilerHttp } = require('@aeternity/aepp-sdk')
const { AeSdk, MemoryAccount, Node } = require('@aeternity/aepp-sdk')
// ES import
import { AeSdk, MemoryAccount, Node, CompilerHttp } from '@aeternity/aepp-sdk'
import { AeSdk, MemoryAccount, Node } from '@aeternity/aepp-sdk'
// additionally you may need to import CompilerCli or CompilerHttp
```

## 2. Setup compiler
Compiler primarily used to generate bytecode to deploy a contract.
Skip this step if you have a contract bytecode or need to interact with an already deployed contract.
Out-of-the-box SDK supports [aesophia_cli](https://github.com/aeternity/aesophia_cli) and [aesophia_http](https://github.com/aeternity/aesophia_http) implemented in [CompilerCli](https://docs.aeternity.com/aepp-sdk-js/v13.2.1/api/classes/CompilerCli.html) and [CompilerHttp](https://docs.aeternity.com/aepp-sdk-js/v13.2.1/api/classes/CompilerHttp.html) respectively.

CompilerCli is available only in Node.js and requires Erlang installed (`escript` available in `$PATH`), Windows is supported.
```js
const compiler = new CompilerCli()
```

## 2. Create an instance of the SDK
CompilerHttp requires a hosted compiler service. Preferable to host your own compiler service since [compiler.aepps.com](https://v7.compiler.aepps.com/api) is planned to be decommissioned. An example of how to run it using [docker-compose](https://github.com/aeternity/aepp-sdk-js/blob/cd8dd7f76a6323383349b48400af0d69c2cfd88e/docker-compose.yml#L11-L14).
```js
const compiler = new CompilerHttp('https://v7.compiler.aepps.com') // host your own compiler
```

Both compiler classes implement the [same interface](https://docs.aeternity.com/aepp-sdk-js/v13.2.1/api/classes/CompilerBase.html) that can be used to generate bytecode and ACI without a Contract instance.

## 3. Create an instance of the SDK
When creating an instance of the SDK you need to provide an account which will be used to sign transactions like `ContractCreateTx` and `ContractCallTx` that will be broadcasted to the network.

```js
Expand All @@ -32,7 +41,7 @@ const account = new MemoryAccount(SECRET_KEY)
const aeSdk = new AeSdk({
nodes: [{ name: 'testnet', instance: node }],
accounts: [account],
onCompiler: new CompilerHttp('https://v7.compiler.aepps.com'), // ideally host your own compiler
onCompiler: compiler, // remove if step #2 skipped
})
```

Expand All @@ -42,7 +51,7 @@ Note:
- For each transaction you can choose a specific account to use for signing (by default the first account will be used), see [transaction options](../transaction-options.md).
- This is specifically important and useful for writing tests.

## 3. Initialize the contract instance
## 4. Initialize the contract instance

### By source code

Expand All @@ -59,6 +68,13 @@ Note:
const contract = await aeSdk.initializeContract({ sourceCode, fileSystem })
```

### By path to source code (available only in Node.js)
It can be used with both CompilerCli and CompilerHttp. This way contract imports would be handled automatically, with no need to provide `fileSystem` option.
```js
const sourceCodePath = './example.aes'
const contract = await aeSdk.initializeContract({ sourceCodePath })
```

### By ACI and bytecode
If you pre-compiled the contracts you can also initialize a contract instance by providing ACI and bytecode:

Expand All @@ -80,7 +96,7 @@ const contract = await aeSdk.initializeContract({ aci, address })
### Options

- Following attributes can be provided via `options` to `initializeContract`:
- `aci` (default: obtained via http compiler)
- `aci` (default: obtained via `onCompiler`)
- The Contract ACI.
- `address`
- The address where the contract is located at.
Expand All @@ -94,7 +110,11 @@ const contract = await aeSdk.initializeContract({ aci, address })
- You wouldn't want to provide an `amount` to each transaction or use the same `nonce` which would result in invalid transactions.
- For options like `ttl` or `gasPrice` it does absolutely make sense to set this on contract instance level.

## 4. Deploy the contract
### Keep bytecode and ACI for future use
After the contract is initialized you can persist values of `contract._aci` and `contract.$options.bytecode`.
They can be provided for subsequent contract initializations to don't depend on a compiler.

## 5. Deploy the contract

If you have a Sophia contract source code that looks like this:
```sophia
Expand Down Expand Up @@ -131,7 +151,7 @@ Note:
- In Sophia all `public functions` are called `entrypoints` and need to be declared as `stateful`
if they should produce changes to the state of the smart contract, see `increment(value: int)`.

## 5. Call contract entrypoints
## 6. Call contract entrypoints

### a) Stateful entrypoints
According to the example above you can call the `stateful` entrypoint `increment` by using one of the following lines:
Expand Down Expand Up @@ -187,21 +207,13 @@ const tx = await contract.$call('fund_project', [1], { amount: 50 })
As already stated various times in the guide it is possible to provide [transaction options](../transaction-options.md) as object to a function of the SDK that builds and potentially broadcasts a transaction. This object can be passed as additional param to each of these functions and overrides the default settings.

## Sophia datatype cheatsheet
Sometimes you might wonder how to pass params to the JavaScript method that calls an entrypoint of your Sophia smart contract. The following table may help you out.

| Type | Sophia entrypoint definition | JavaScript method call |
|-----------|-----------------------------------|--------------------------------------------------|
| int | `add_two(one: int, two: int)` | `add_two(1 , 2)` |
| address | `set_owner(owner: address)` | `set_owner('ak_1337...')` |
| bool | `is_it_true(answer: bool)` | `is_it_true(true)` |
| bits | `give_me_bits(input: bits)` | `give_me_bits(0b10110n)` |
| bytes | `get_bytes(test: bytes(3))` | `get_bytes(new Uint8Array([0x1, 0x1f, 0x10]))` |
| string | `hello_world(say_hello: string)` | `hello_world('Hello!')` |
| list | `have_a_few(candy: list(string))` | `have_a_few(['Skittles', 'M&Ms', 'JellyBelly'])` |
| tuple | `a_few_things(things: (string * int * map(address, bool)))` | `a_few_things(['hola', 3, new Map([['ak_1337...', true]])])` |
| record | `record user = {`<br />&nbsp; &nbsp; &nbsp; &nbsp; `firstname: string,` <br /> &nbsp; &nbsp; &nbsp; &nbsp; `lastname: string` <br /> `}` <br /> <br /> `get_firstname(input: user): string` | `get_firstname({'firstname': 'Alfred', 'lastname': 'Mustermann'})` |
| map | `balances(values: map(address, int))` | `balances(new Map([['ak_1337...', 123], ['ak_FCKGW...', 321], ['ak_Rm5U...', 999]]))` |
| option() | `number_defined(value: option(int)): bool = `<br /> &nbsp; &nbsp; &nbsp; &nbsp; `Option.is_some(value)` | `// the datatype in the option()` <br /> `number_defined(1337) // int in this case` |
| hash | `a_gram(of: hash)` | `// 32 bytes signature` <br /> `a_gram('af01...490f')` |
| signature | `one_signature(sig: signature)` | `// 64 bytes signature` <br /> `one_signature('af01...490f')` |
| functions | (Higher order) functions are not allowed in `entrypoint` params | |
Sometimes you might wonder how to pass params to the JavaScript method that calls an entrypoint of your Sophia smart contract.
The conversion between JS and Sophia values is handled by aepp-calldata library.
Refer to [its documentation](https://www.npmjs.com/package/@aeternity/aepp-calldata#data-types) to find the right type to use.

## Generate file system object in Node.js
To do so you can use [getFileSystem](https://docs.aeternity.com/aepp-sdk-js/v13.2.1/api/functions/getFileSystem.html) function.
In most cases, you don't need to do it explicitly. Prefer to use `sourceCodePath` instead `sourceCode` in
[Contract::initialize](https://docs.aeternity.com/aepp-sdk-js/v13.2.1/api/classes/_internal_.Contract.html#initialize),
and [compile](https://docs.aeternity.com/aepp-sdk-js/v13.2.1/api/classes/CompilerBase.html#compile)
instead [compileBySourceCode](https://docs.aeternity.com/aepp-sdk-js/v13.2.1/api/classes/CompilerBase.html#compileBySourceCode) in CompilerBase.
4 changes: 2 additions & 2 deletions docs/guides/paying-for-tx.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ You can then collect the signed inner transaction, wrap it into a `PayingForTx`
## Usage examples
We provided following two NodeJS examples which you can take a look at:

- [InnerTx: ContractCallTx](https://docs.aeternity.com/aepp-sdk-js/v13.1.0/examples/node/paying-for-contract-call-tx/)
- [InnerTx: SpendTx](https://docs.aeternity.com/aepp-sdk-js/v13.1.0/examples/node/paying-for-spend-tx/)
- [InnerTx: ContractCallTx](https://docs.aeternity.com/aepp-sdk-js/v13.2.1/examples/node/paying-for-contract-call-tx/)
- [InnerTx: SpendTx](https://docs.aeternity.com/aepp-sdk-js/v13.2.1/examples/node/paying-for-spend-tx/)

Note:

Expand Down
2 changes: 1 addition & 1 deletion examples/node/transfer-ae.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ console.log(`Balance of ${recipient} (before): ${balanceBefore} aettos`);
// Calling the `spend` function will create, sign and broadcast a `SpendTx` to the network.
const tx = await aeSdk.spend(amount, recipient);
console.log('Transaction mined', tx);
// Alternatively, you can use [transferFunds](https://docs.aeternity.com/aepp-sdk-js/v13.1.0/api/functions/transferFunds.html)
// Alternatively, you can use [transferFunds](https://docs.aeternity.com/aepp-sdk-js/v13.2.1/api/functions/transferFunds.html)
// method to transfer a fraction of your AE to another account.

// ## 6. Get AE balance of recipient (after transfer)
Expand Down
2 changes: 1 addition & 1 deletion src/Middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export default class Middleware
/**
* @param url - Url for middleware API
* @param options - Options
* @param options.ignoreVersion - Don't check node version
* @param options.ignoreVersion - Don't ensure that the middleware is supported
* @param options.retryCount - Amount of extra requests to do in case of failure
* @param options.retryOverallDelay - Time in ms to wait between all retries
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export default class Node extends (NodeTransformed as unknown as NodeTransformed
/**
* @param url - Url for node API
* @param options - Options
* @param options.ignoreVersion - Don't check node version
* @param options.ignoreVersion - Don't ensure that the node is supported
* @param options.retryCount - Amount of extra requests to do in case of failure
* @param options.retryOverallDelay - Time in ms to wait between all retries
*/
Expand Down
9 changes: 7 additions & 2 deletions src/contract/compiler/Cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,19 @@ const getPackagePath = (): string => {
};

/**
* A wrapper around aesophia_cli, available only in Node.js
* Assumes that `escript` is available in PATH.
* A wrapper around aesophia_cli, available only in Node.js.
* Requires Erlang installed, assumes that `escript` is available in PATH.
*/
export default class CompilerCli extends CompilerBase {
#path: string;

#ensureCompatibleVersion = Promise.resolve();

/**
* @param compilerPath - A path to aesophia_cli binary, by default uses the integrated one
Copy link
Contributor

@marc0olo marc0olo Jul 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"integrated one" means that the CLI is actually shipped along with the SDK. do I understand correctly? I think it should be made clear to the reader that this is actually the case. I didn't know that 😅

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume that windows is not supported here using the CLI compiler, right? 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do I understand correctly?

Yep.

I've checked it before, it should be working in windows.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's cool. wouldn't have expected the CLI binary of the compiler to work with windows

Copy link
Member Author

@davidyuk davidyuk Jul 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is an Erlang VM binary as I understand

* @param options - Options
* @param options.ignoreVersion - Don't ensure that the compiler is supported
*/
constructor(
compilerPath = resolve(getPackagePath(), './bin/aesophia_cli'),
{ ignoreVersion }: { ignoreVersion?: boolean } = {},
Expand Down
Loading