The WebAssembly binaries for the bytecode can be built and published using steps from the book, summarized below.
Before getting started, make sure that the binary tools linera*
corresponding to
your version of linera-sdk
are in your PATH. For scripting purposes, we also assume
that the BASH function linera_spawn_and_read_wallet_variables
is defined.
Assuming the Linera source code v0.10.1 in ../linera-protocol
, this can be achieved as follows:
export PATH="$PWD/../linera-protocol/target/debug:$PATH"
source /dev/stdin <<<"$(linera net helper 2>/dev/null)"
You may also use cargo install linera-service@0.10.1
and append the output of
linera net helper
to your ~/.bash_profile
.
Now, we are ready to set up a local network with an initial wallet owning several initial chains. In a new BASH shell, enter:
linera_spawn_and_read_wallet_variables linera net up --testing-prng-seed 37
A new test network is now running and the environment variables LINERA_WALLET
and
LINERA_STORAGE
are now be defined for the duration of the shell session. We used the
test-only CLI option --testing-prng-seed
to make keys deterministic and simplify our
presentation.
Now, compile the fungible
application WebAssembly binaries, and publish them as an application
bytecode:
cargo build --release
BYTECODE_ID=$(linera publish-bytecode \
target/wasm32-unknown-unknown/release/fungible_{contract,service}.wasm)
Here, we stored the new bytecode ID in a variable BYTECODE_ID
to be reused it later.
In order to use the published bytecode to create a token application, the initial state must be specified. This initial state is where the tokens are minted. After the token is created, no additional tokens can be minted and added to the application. The initial state is a JSON string that specifies the accounts that start with tokens.
In order to select the accounts to have initial tokens, the command below can be used to list the chains created for the test in the default wallet:
linera wallet show
A table will be shown with the chains registered in the wallet and their meta-data. The default
chain should be highlighted in green. Each chain has an Owner
field, and that is what is used
for the account.
Let's define some variables corresponding to these values. (Note that owner addresses
would not be predictable without --testing-prng-seed
above.)
CHAIN_1=e476187f6ddfeb9d588c7b45d3df334d5501d6499b3f9ad5595cae86cce16a65 # default chain for the wallet
OWNER_1=7136460f0c87ae46f966f898d494c4b40c4ae8c527f4d1c0b1fa0f7cff91d20f # owner of chain 1
CHAIN_2=256e1dbc00482ddd619c293cc0df94d366afe7980022bb22d99e33036fd465dd # another chain in the wallet
OWNER_2=598d18f67709fe76ed6a36b75a7c9889012d30b896800dfd027ee10e1afd49a3 # owner of chain 2
The example below creates a token application on the default chain CHAIN_1 and gives the owner 100 tokens:
APP_ID=$(linera create-application $BYTECODE_ID \
--json-argument "\"100.\""
)
This will store the application ID in a new variable APP_ID
.
Before using the token, a source and target address should be selected. The source address should ideally be on the default chain (used to create the token) and one of the accounts chosen for the initial state, because it will already have some initial tokens to send.
First, a node service for the current wallet has to be started:
PORT=8080
linera service --port $PORT &
- Navigate to
http://localhost:8080/chains/$CHAIN_1/applications/$APP_ID
. - To get the current balance of user $OWNER_1, run the query:
query{
accounts {
entry(
key: "7136460f0c87ae46f966f898d494c4b40c4ae8c527f4d1c0b1fa0f7cff91d20f"
) {
value
}
}
}
- To get the current balance of user $OWNER_2, run the query:
query{
accounts {
entry(
key: "598d18f67709fe76ed6a36b75a7c9889012d30b896800dfd027ee10e1afd49a3"
) {
value
}
}
}
- To transfer 50 tokens from $OWNER_1 to $OWNER_2
mutation {
transfer(
owner: "7136460f0c87ae46f966f898d494c4b40c4ae8c527f4d1c0b1fa0f7cff91d20f",
amount: "50.",
targetAccount: {
chainId: "e476187f6ddfeb9d588c7b45d3df334d5501d6499b3f9ad5595cae86cce16a65",
owner: "598d18f67709fe76ed6a36b75a7c9889012d30b896800dfd027ee10e1afd49a3"
}
)
}
- To get the new balance of user $OWNER_1, run the query:
query{
accounts {
entry(
key: "7136460f0c87ae46f966f898d494c4b40c4ae8c527f4d1c0b1fa0f7cff91d20f"
) {
value
}
}
}
- To get the new balance of user $OWNER_2, run the query:
query{
accounts {
entry(
key: "598d18f67709fe76ed6a36b75a7c9889012d30b896800dfd027ee10e1afd49a3"
) {
value
}
}
}
Installing and starting the web server:
cd web-frontend
npm install --no-save
# Start the server but not open the web page right away.
BROWSER=none npm start &
Web UIs for specific accounts can be opened by navigating URLs of the form
http://localhost:3000/$CHAIN?app=$APP_ID&owner=$OWNER&port=$PORT
where
- the path is the ID of the chain where the account is located.
- the argument
app
is the token application ID obtained when creating the token. owner
is the address of the chosen user account (owner must be have permissions to create blocks in the given chain).port
is the port of the wallet service (the wallet must know the secret key ofowner
).
In this example, two web pages for OWNER_1 and OWNER_2 can be opened by navigating these URLs:
echo "http://localhost:3000/$CHAIN_1?app=$APP_ID&owner=$OWNER_1&port=$PORT"
echo "http://localhost:3000/$CHAIN_1?app=$APP_ID&owner=$OWNER_2&port=$PORT"
OWNER_2 doesn't have the applications loaded initially. Using the first page to transfer tokens from OWNER_1 to OWNER_2 at CHAIN_2 will instantly update the UI of the second page.