Quarter I 2022
原文作者:Ansem(推特@blknoiz06) 原文链接:Quarter I 2022always keep a few gems in your pockethttps://blknoiz06.substack.com译者:Evelyn、AluAyi、Henson、Rex|W3.Hitchhiker2022市场整体展望你好!对于我的堕落同胞(fellow degenerates)和其他不知何故闯入这个页面的读者们,我将尝试在这里梳理今年一季度的一些想法。这是我第二次写这样的长篇大论,所以请忍耐一下(哈哈),希望它比从我推特发出来的数百条零零散散的推文更有条理。 2021年对于加密资产来说很显然是突破性的一年,从机构接受度到散户采纳度,加密货币已经远远超过了其历史上的任何时期。加密资产经历了两年的强劲牛市,部分原因是FED的鸽派态度助长了市场参与者的风险偏好,再加上web3协议的快速创新。令人震惊的是,在这个周期中,我们能从这则典型的新闻吹嘘 "看这个代币上涨了10倍"体会到加密货币在社会中的流行程度。Visa购置了一个朋克,阿迪达斯买了一只无聊猴,许多TradFi公司如Jum...

Arweave—不仅仅是存储这么简单
项目简介Arweave 是个非典型或非主流方向的区块链项目,我们之前对其一无所知,稍有了解的人,可能也就是把它看作是众多类似 Filecoin 的去中心化存储项目之一。所以也造成了之前我们对Arweave的一些忽视,今天就好好的把整个项目重新再认识一下。 Arweave 协议的愿景是提供去中心化、可扩展和永久的链上数据存储(永存);就像以太坊被认为是世界计算机,Arweave 可以被认为是永远不会忘记的世界硬盘。创始人Arweave创始人兼CEO—Sam Williams是去中心化爱好者,在分布式系统设计和实施方面具有丰富的经验。英国人,黑客,博士,目前推特关注者15K。先简单了解Filecoin的方案Filecoin是去中心化存储赛道最知名的项目,我们聊Arweave肯定免不了先来了解下Filecoin。 首先,我们知道,Protocol Labs开创的IPFS 是去中心化存储领域的开创者 ,从 2014 年上线开始,自由生长,已经存储了大量的数据。但是要让 IPFS 成为商业可用的存储系统,而不是随意的数据分享平台,必须提供服务质量保障 ,这就是 Filecoin 要解决的问...

Lyra - Option AMM Pioneer
Created by Jack Ding|W3.HitchhikerAn Overview of On-chain OptionsAt present, there are three main types of on-chain options:OrderbookDeFi Option Vaults(structured products)Auto-market Marker(resource:https://www.panewslab.com/zh/articledetails/1644045861942159.html)Orderbooke.x.:Zeta,Psyoption,Opyn Option order books also require high tps blockchain to guarantee cheap and fast execution. Opyn used the Orderbook model on ETH main chain, but due to the lack of liquidity and high gas costs,trade...
W3.Hitchhiker aims to discover cutting-edge tech and innovative teams by first principles thinking and long-term analysis strategies.
Quarter I 2022
原文作者:Ansem(推特@blknoiz06) 原文链接:Quarter I 2022always keep a few gems in your pockethttps://blknoiz06.substack.com译者:Evelyn、AluAyi、Henson、Rex|W3.Hitchhiker2022市场整体展望你好!对于我的堕落同胞(fellow degenerates)和其他不知何故闯入这个页面的读者们,我将尝试在这里梳理今年一季度的一些想法。这是我第二次写这样的长篇大论,所以请忍耐一下(哈哈),希望它比从我推特发出来的数百条零零散散的推文更有条理。 2021年对于加密资产来说很显然是突破性的一年,从机构接受度到散户采纳度,加密货币已经远远超过了其历史上的任何时期。加密资产经历了两年的强劲牛市,部分原因是FED的鸽派态度助长了市场参与者的风险偏好,再加上web3协议的快速创新。令人震惊的是,在这个周期中,我们能从这则典型的新闻吹嘘 "看这个代币上涨了10倍"体会到加密货币在社会中的流行程度。Visa购置了一个朋克,阿迪达斯买了一只无聊猴,许多TradFi公司如Jum...

Arweave—不仅仅是存储这么简单
项目简介Arweave 是个非典型或非主流方向的区块链项目,我们之前对其一无所知,稍有了解的人,可能也就是把它看作是众多类似 Filecoin 的去中心化存储项目之一。所以也造成了之前我们对Arweave的一些忽视,今天就好好的把整个项目重新再认识一下。 Arweave 协议的愿景是提供去中心化、可扩展和永久的链上数据存储(永存);就像以太坊被认为是世界计算机,Arweave 可以被认为是永远不会忘记的世界硬盘。创始人Arweave创始人兼CEO—Sam Williams是去中心化爱好者,在分布式系统设计和实施方面具有丰富的经验。英国人,黑客,博士,目前推特关注者15K。先简单了解Filecoin的方案Filecoin是去中心化存储赛道最知名的项目,我们聊Arweave肯定免不了先来了解下Filecoin。 首先,我们知道,Protocol Labs开创的IPFS 是去中心化存储领域的开创者 ,从 2014 年上线开始,自由生长,已经存储了大量的数据。但是要让 IPFS 成为商业可用的存储系统,而不是随意的数据分享平台,必须提供服务质量保障 ,这就是 Filecoin 要解决的问...

Lyra - Option AMM Pioneer
Created by Jack Ding|W3.HitchhikerAn Overview of On-chain OptionsAt present, there are three main types of on-chain options:OrderbookDeFi Option Vaults(structured products)Auto-market Marker(resource:https://www.panewslab.com/zh/articledetails/1644045861942159.html)Orderbooke.x.:Zeta,Psyoption,Opyn Option order books also require high tps blockchain to guarantee cheap and fast execution. Opyn used the Orderbook model on ETH main chain, but due to the lack of liquidity and high gas costs,trade...
W3.Hitchhiker aims to discover cutting-edge tech and innovative teams by first principles thinking and long-term analysis strategies.


Share Dialog
Share Dialog

Subscribe to W3.Hitchhiker

Subscribe to W3.Hitchhiker
>100 subscribers
>100 subscribers
Written by Dan
Revised by Evelyn
Chinese Version:《使用 Warp 部署一个 PST 代币合约》
In this tutorial we're going to learn how to build a simple PST (Profit Sharing Token) application. In order to achieve that, we will write our first PST contract. After doing some testing on the user's local test network, we will deploy it to the Redstone public testnet. It will let us mint some tokens as well as transfer them between addresses and read current balances. We will then create a simple dApp which will help us interact with the contract in a user-friendly way.
installed node.js 16.5 or higher
installed yarn
installed visual studio code
installed python3
installed git
git clone https://github.com/warp-contracts/academy.git

cd academy/warp-academy-pst/challenge

3. Launch visual studio code:

yarn add arweave@1.11.4 arlocal@1.1.42 warp-contracts@1.0.1

Head to warp-academy-pst/challenge/src/contracts/types/types.ts and let's start writing! Copy the following code:
export interface PstState {
ticker: string;
name: string;
owner: string;
balances: {
[address: string]: number,
};
}

PstState represents the contract's current state. Its shape is not defined by any rules and it is the developer who decides what the state will look like. In case of our implementation it will consist of four properties:
ticker — abbreviation of token name.
name — name of the token.
owner — owner of the token.
balances — define the token balance of the address.
Head to warp-academy-pst/challenge/src/contracts/initial-state.json, copy the following code:
{
"ticker": "FC",
"name": "Federation Credits",
"owner": "GH2IY_3vtE2c0KfQve9_BHoIPjZCS8s5YmSFS_fppKI",
"balances": {
"GH2IY_3vtE2c0KfQve9_BHoIPjZCS8s5YmSFS_fppKI": 1000,
"33F0QHcb22W7LwWR1iRC8Az1ntZG09XQ03YWuw2ABqA": 230
}
}
3. Edit the source code of the contract
Let's talk about contract. It exports one function — handle — which accepts two arguments:
state — contract's current state.
action — contract interaction with two properties:
caller — wallet address of user interacting with the contract.
input — user's input to the contract.
Handle function should end by:
returning { state: newState } — when contract state is changing after specific interaction.
returning { result: someResult } — when contract state is not changing after interaction.
throwing ContractError exception.
Head to warp-academy-pst/challenge/src/contracts/types/types.ts, add the following code:
export interface PstAction {
input: PstInput;
caller: string;
}
export interface PstInput {
function: PstFunction;
target: string;
qty: number;
}
export interface PstResult {
target: string;
ticker: string;
balance: number;
}
export type PstFunction = 'transfer' | 'mint' | 'balance';
export type ContractResult = { state: PstState } | { result: PstResult };
PstAction represents contract's interaction. As mentioned earlier it has two properties — caller and input. In our contract user will have an ability to write three types of inputs (PstInput):
function — type of interaction (in our case, it can be transfering tokens, minting tokens or reading balances - PstFunction)
target — target address.
qty — amount of tokens to be transferred/minted.
PstResult — object returned when the contract state has not changed after an interaction:
target — target address.
ticker — an abbreviation used to uniquely identify the token.
balance — specific address balance.
ContractResult — contract's handler function should be terminated by one of those:
state — when the state is being changed
result — when the interaction was a read-only operation
Head to warp-academy-pst/challenge/src/contracts/actions/read/balance.ts, add the following code:
declare const ContractError;
export const balance = async (
state: PstState,
{ input: { target } }: PstAction
): Promise<ContractResult> => {
const ticker = state.ticker;
const balances = state.balances;
if (typeof target !== 'string') {
throw new ContractError('Must specify target to get balance for');
}
if (typeof balances[target] !== 'number') {
throw new ContractError('Cannot get balance, target does not exist');
}
return { result: { target, ticker, balance: balances[target] } };
};
The above function will help us read the balance of the inidicated target address.
Head to warp-academy-pst/challenge/src/contracts/actions/write/mintTokens.ts,
add the following code:
declare const ContractError;
export const mintTokens = async (
state: PstState,
{ caller, input: { qty } }: PstAction
): Promise<ContractResult> => {
const balances = state.balances;
if (qty <= 0) {
throw new ContractError('Invalid token mint');
}
if (!Number.isInteger(qty)) {
throw new ContractError('Invalid value for "qty". Must be an integer');
}
balances[caller] ? (balances[caller] += qty) : (balances[caller] = qty);
return { state };
};
This one will help us minting some tokens to the caller's address.
Head to warp-academy-pst/challenge/src/contracts/actions/write/transferTokens.ts, add the following code:
declare const ContractError;
export const transferTokens = async (
state: PstState,
{ caller, input: { target, qty } }: PstAction
): Promise<ContractResult> => {
const balances = state.balances;
if (!Number.isInteger(qty)) {
throw new ContractError('Invalid value for "qty". Must be an integer');
}
if (!target) {
throw new ContractError('No target specified');
}
if (qty <= 0 || caller === target) {
throw new ContractError('Invalid token transfer');
}
if (!balances[caller]) {
throw new ContractError(`Caller balance is not defined!`);
}
if (balances[caller] < qty) {
throw new ContractError(
`Caller balance not high enough to send ${qty} token(s)!`
);
}
balances[caller] -= qty;
if (target in balances) {
balances[target] += qty;
} else {
balances[target] = qty;
}
return { state };
};
Head to warp-academy-pst/challenge/src/contracts/contract.ts, add the following code:
import { balance } from './actions/read/balance';
import { mintTokens } from './actions/write/mintTokens';
import { transferTokens } from './actions/write/transferTokens';
import { PstAction, PstResult, PstState } from './types/types';
declare const ContractError;
export async function handle(
state: PstState,
action: PstAction
): Promise<ContractResult> {
const input = action.input;
switch (input.function) {
case 'mint':
return await mintTokens(state, action);
case 'transfer':
return await transferTokens(state, action);
case 'balance':
return await balance(state, action);
default:
throw new ContractError(
`No function supplied or function not recognised: "${input.function}"`
);
}
}
Handle function is an asynchronous function and it returns a promise of type ContractResult. As mentioned above, it takes two arguments — state and action. It waits for one of the interactions to be called and returns the result of matching functions — the ones that we prepared earlier.
Run the following command :yarn build:contracts

Head to warp-academy-pst/challenge/tests/contract.test.ts, copy the following code:
The above code launches a local test network for AR, creates an AR wallet, deploys the contract on the local test network, and tests features such as minting tokens and transferring tokens.
Head to warp-academy-pst\challenge\utils\_helpers.ts, copy the following code:
import Arweave from 'arweave';
import { JWKInterface } from 'arweave/node/lib/wallet';
// ~~ Write function responsible for adding funds to the generated wallet ~~
export async function addFunds(arweave: Arweave, wallet: JWKInterface) {
const walletAddress = await arweave.wallets.getAddress(wallet);
await arweave.api.get(`/mint/${walletAddress}/1000000000000000`);
}
// ~~ Write function responsible for mining block on the Arweave testnet ~~
export async function mineBlock(arweave: Arweave) {
await arweave.api.get('mine');
}
Run the following command: yarn test:node


The above screenshot shows the six test cases are running successfully on local testnet.
Head to warp-academy-pst\challenge\src\tools\deploy-test-contract.ts, copy the following code:
import Arweave from 'arweave';
import { JWKInterface } from 'arweave/node/lib/wallet';
import { PstState } from '../contracts/types/types';
import { LoggerFactory, PstContract, Warp, WarpNodeFactory } from 'warp-contracts';
import fs from 'fs';
import path from 'path';
import { addFunds, mineBlock } from '../../utils/_helpers';
let contractSrc: string;
let wallet: JWKInterface;
let walletAddress: string;
let initialState: PstState;
let arweave: Arweave;
let warp: Warp;
(async () => {
// ~~ Declare variables ~~
// ~~ Initialize Arweave ~~
arweave = Arweave.init({
host: 'testnet.redstone.tools',
port: 443,
protocol: 'https',
});
// ~~ Initialize `LoggerFactory` ~~
LoggerFactory.INST.logLevel('error');
// ~~ Initialize Warp ~~
warp = WarpNodeFactory.memCached(arweave);
// ~~ Generate wallet and add some funds ~~
wallet = await arweave.wallets.generate();
walletAddress = await arweave.wallets.jwkToAddress(wallet);
await addFunds(arweave, wallet);
// ~~ Read contract source and initial state files ~~
contractSrc = fs.readFileSync(
path.join(__dirname, '../../dist/contract.js'),
'utf8'
);
const stateFromFile: PstState = JSON.parse(
fs.readFileSync(
path.join(__dirname, '../../dist/contracts/initial-state.json'),
'utf8'
)
);
// ~~ Override contract's owner address with the generated wallet address ~~
initialState = {
...stateFromFile,
...{
owner: walletAddress,
},
};
// ~~ Deploy contract ~~
const contractTxId = await warp.createContract.deploy({
wallet,
initState: JSON.stringify(initialState),
src: contractSrc,
});
// ~~ Log contract id to the console ~~
console.log(contractTxId);
//Mine block
await mineBlock(arweave);
})();
The above code will generate and fund the Arweave wallet, read, contract source and initial state files and deploy the contract to the testnet. After the contract is deployed successfully, you will be able to see the contract address in the command line.
Run the following comand:yarn ts-node src/tools/deploy-test-contract.ts

The above screenshot shows the contract is deployed successfully to redstone testnet, and the contract address is:
XeJiSHIkj0dVU7ddGtOIE8kZoQEIPcqvLS_y5KPu62w
As it is not a frontend tutorial, we've already got you covered. We will be working with Vue v.2 with typescript support.
To quickly walk you through the structure of the project:
challenge/src/main.ts — a starting point for the application.
challenge/src/pst-contract.ts — here we define Arweave and SmartWeave instances and export them.
challenge/src/deployed-contracts.ts — here we indicate deployed contract id.
challenge/src/constants.ts — all the constants (including urls).
challenge/src/assets — all the assets used in the application.
challenge/src/components — all the components that are key features of Vue to encapsulate reusable code.
challenge/src/router — router of the application build with vue-router.
Head to challenge/src/pst-contract.ts, copy the following code:
import Arweave from 'arweave';
import {
PstContract,
PstState,
Warp,
WarpNodeFactory,
LoggerFactory,
InteractionResult,
WarpWebFactory
} from 'warp-contracts';
export const arweave: Arweave = Arweave.init({
host: 'testnet.redstone.tools',
port: 443,
protocol: 'https',
});
export const warp: Warp = WarpWebFactory.memCachedBased(arweave).useArweaveGateway().build();
Head to challenge\src\deployed-contracts.ts, add the contract which is deployed successfully in step 7.

The contract address should be the contract that you deploy successfully to redstone testnet.
Head to challenge\src\constants.ts, copy the following code:
export const url = {
warpGateway: '[https://gateway.redstone.finance](https://gateway.redstone.finance)',
};
Head to challenge\src\store\index.ts,copy the following code,
import Vue from 'vue';
import Vuex from 'vuex';
import { arweave, warp } from '../pst-contract';
import { deployedContracts } from '../deployed-contracts';
import { PstState } from '@/contracts/types/types';
import { Contract } from 'warp-contracts';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
arweave,
warp,
state: {},
validity: {},
contract: null,
walletAddress: null,
},
mutations: {
setState(state, swState) {
state.state = swState;
},
setValidity(state, validity) {
state.validity = validity;
},
setContract(state, contract) {
state.contract = contract;
},
setWalletAddress(state, walletAddress) {
state.walletAddress = walletAddress;
},
},
actions: {
async loadState({ commit }) {
// ~~ Generate arweave wallet ~~
const wallet = await arweave.wallets.generate();
// ~~ Get wallet address and mint some tokens ~~
const walletAddress = await arweave.wallets.getAddress(wallet);
await arweave.api.get(`/mint/${walletAddress}/1000000000000000`);
// ~~ Connect deployed contract and wallet ~~
const contract: Contract = warp
.pst(deployedContracts.fc)
.connect(wallet);
commit('setContract', contract);
// ~~ Set the state of the contract ~~
const { state, validity } = await contract.readState();
commit('setState', state);
commit('setValidity', validity);
commit('setWalletAddress', walletAddress);
},
},
modules: {},
});
Head to challenge\src\components\Header\Header.vue, insert the following code:
const txId = await this.contract.writeInteraction({
function: 'mint',
qty: parseInt(this.$refs.balanceMint.value),
});
await this.arweave.api.get('mine');
// ~~ Set the balances by calling `currentState` method ~~
const newResult = await this.contract.currentState();
Please insert the code as the below screenshot:

Head to challenge\src\components\BalancesList\BalancesList.vue, insert the following code:
const tx = await this.contract.transfer({
target: address,
qty: parseInt(qty),
});
// ~~ Mine a block ~~
await this.arweave.api.get('mine');
// ~~ Set new balances list by calling `currentState` method
let newResult = await this.contract.currentState();
Please insert the code as the below screenshot:

By now, all source code editing is done.
Type yarn build at the command line to build the entire project, and the following screenshot shows a successful build:


Type yarn serve at the command line to run it in the development environment, and the following screenshot shows a successful run:

Open your browser and go to http://localhost:8080/,

Now open the interface, it will display the contract address, wallet address, and token balance information. At this point, you can mint some of the tokens first and then transfer them.

After some token minting and transfer, the PST token balances are updated.
Disclaimer: The content of this article is for information and communication purposes only and does not constitute any investment advice. If there are obvious errors of understanding or data, welcome feedback.
The content of this article was originally created by W3.Hitchhiker, please indicate the source if you need to reproduce.
Business cooperation: hello@w3hitchhiker.com
Official website: https://w3hitchhiker.com/
W3.Hitchhiker official Twitter: https://twitter.com/HitchhikerW3
Written by Dan
Revised by Evelyn
Chinese Version:《使用 Warp 部署一个 PST 代币合约》
In this tutorial we're going to learn how to build a simple PST (Profit Sharing Token) application. In order to achieve that, we will write our first PST contract. After doing some testing on the user's local test network, we will deploy it to the Redstone public testnet. It will let us mint some tokens as well as transfer them between addresses and read current balances. We will then create a simple dApp which will help us interact with the contract in a user-friendly way.
installed node.js 16.5 or higher
installed yarn
installed visual studio code
installed python3
installed git
git clone https://github.com/warp-contracts/academy.git

cd academy/warp-academy-pst/challenge

3. Launch visual studio code:

yarn add arweave@1.11.4 arlocal@1.1.42 warp-contracts@1.0.1

Head to warp-academy-pst/challenge/src/contracts/types/types.ts and let's start writing! Copy the following code:
export interface PstState {
ticker: string;
name: string;
owner: string;
balances: {
[address: string]: number,
};
}

PstState represents the contract's current state. Its shape is not defined by any rules and it is the developer who decides what the state will look like. In case of our implementation it will consist of four properties:
ticker — abbreviation of token name.
name — name of the token.
owner — owner of the token.
balances — define the token balance of the address.
Head to warp-academy-pst/challenge/src/contracts/initial-state.json, copy the following code:
{
"ticker": "FC",
"name": "Federation Credits",
"owner": "GH2IY_3vtE2c0KfQve9_BHoIPjZCS8s5YmSFS_fppKI",
"balances": {
"GH2IY_3vtE2c0KfQve9_BHoIPjZCS8s5YmSFS_fppKI": 1000,
"33F0QHcb22W7LwWR1iRC8Az1ntZG09XQ03YWuw2ABqA": 230
}
}
3. Edit the source code of the contract
Let's talk about contract. It exports one function — handle — which accepts two arguments:
state — contract's current state.
action — contract interaction with two properties:
caller — wallet address of user interacting with the contract.
input — user's input to the contract.
Handle function should end by:
returning { state: newState } — when contract state is changing after specific interaction.
returning { result: someResult } — when contract state is not changing after interaction.
throwing ContractError exception.
Head to warp-academy-pst/challenge/src/contracts/types/types.ts, add the following code:
export interface PstAction {
input: PstInput;
caller: string;
}
export interface PstInput {
function: PstFunction;
target: string;
qty: number;
}
export interface PstResult {
target: string;
ticker: string;
balance: number;
}
export type PstFunction = 'transfer' | 'mint' | 'balance';
export type ContractResult = { state: PstState } | { result: PstResult };
PstAction represents contract's interaction. As mentioned earlier it has two properties — caller and input. In our contract user will have an ability to write three types of inputs (PstInput):
function — type of interaction (in our case, it can be transfering tokens, minting tokens or reading balances - PstFunction)
target — target address.
qty — amount of tokens to be transferred/minted.
PstResult — object returned when the contract state has not changed after an interaction:
target — target address.
ticker — an abbreviation used to uniquely identify the token.
balance — specific address balance.
ContractResult — contract's handler function should be terminated by one of those:
state — when the state is being changed
result — when the interaction was a read-only operation
Head to warp-academy-pst/challenge/src/contracts/actions/read/balance.ts, add the following code:
declare const ContractError;
export const balance = async (
state: PstState,
{ input: { target } }: PstAction
): Promise<ContractResult> => {
const ticker = state.ticker;
const balances = state.balances;
if (typeof target !== 'string') {
throw new ContractError('Must specify target to get balance for');
}
if (typeof balances[target] !== 'number') {
throw new ContractError('Cannot get balance, target does not exist');
}
return { result: { target, ticker, balance: balances[target] } };
};
The above function will help us read the balance of the inidicated target address.
Head to warp-academy-pst/challenge/src/contracts/actions/write/mintTokens.ts,
add the following code:
declare const ContractError;
export const mintTokens = async (
state: PstState,
{ caller, input: { qty } }: PstAction
): Promise<ContractResult> => {
const balances = state.balances;
if (qty <= 0) {
throw new ContractError('Invalid token mint');
}
if (!Number.isInteger(qty)) {
throw new ContractError('Invalid value for "qty". Must be an integer');
}
balances[caller] ? (balances[caller] += qty) : (balances[caller] = qty);
return { state };
};
This one will help us minting some tokens to the caller's address.
Head to warp-academy-pst/challenge/src/contracts/actions/write/transferTokens.ts, add the following code:
declare const ContractError;
export const transferTokens = async (
state: PstState,
{ caller, input: { target, qty } }: PstAction
): Promise<ContractResult> => {
const balances = state.balances;
if (!Number.isInteger(qty)) {
throw new ContractError('Invalid value for "qty". Must be an integer');
}
if (!target) {
throw new ContractError('No target specified');
}
if (qty <= 0 || caller === target) {
throw new ContractError('Invalid token transfer');
}
if (!balances[caller]) {
throw new ContractError(`Caller balance is not defined!`);
}
if (balances[caller] < qty) {
throw new ContractError(
`Caller balance not high enough to send ${qty} token(s)!`
);
}
balances[caller] -= qty;
if (target in balances) {
balances[target] += qty;
} else {
balances[target] = qty;
}
return { state };
};
Head to warp-academy-pst/challenge/src/contracts/contract.ts, add the following code:
import { balance } from './actions/read/balance';
import { mintTokens } from './actions/write/mintTokens';
import { transferTokens } from './actions/write/transferTokens';
import { PstAction, PstResult, PstState } from './types/types';
declare const ContractError;
export async function handle(
state: PstState,
action: PstAction
): Promise<ContractResult> {
const input = action.input;
switch (input.function) {
case 'mint':
return await mintTokens(state, action);
case 'transfer':
return await transferTokens(state, action);
case 'balance':
return await balance(state, action);
default:
throw new ContractError(
`No function supplied or function not recognised: "${input.function}"`
);
}
}
Handle function is an asynchronous function and it returns a promise of type ContractResult. As mentioned above, it takes two arguments — state and action. It waits for one of the interactions to be called and returns the result of matching functions — the ones that we prepared earlier.
Run the following command :yarn build:contracts

Head to warp-academy-pst/challenge/tests/contract.test.ts, copy the following code:
The above code launches a local test network for AR, creates an AR wallet, deploys the contract on the local test network, and tests features such as minting tokens and transferring tokens.
Head to warp-academy-pst\challenge\utils\_helpers.ts, copy the following code:
import Arweave from 'arweave';
import { JWKInterface } from 'arweave/node/lib/wallet';
// ~~ Write function responsible for adding funds to the generated wallet ~~
export async function addFunds(arweave: Arweave, wallet: JWKInterface) {
const walletAddress = await arweave.wallets.getAddress(wallet);
await arweave.api.get(`/mint/${walletAddress}/1000000000000000`);
}
// ~~ Write function responsible for mining block on the Arweave testnet ~~
export async function mineBlock(arweave: Arweave) {
await arweave.api.get('mine');
}
Run the following command: yarn test:node


The above screenshot shows the six test cases are running successfully on local testnet.
Head to warp-academy-pst\challenge\src\tools\deploy-test-contract.ts, copy the following code:
import Arweave from 'arweave';
import { JWKInterface } from 'arweave/node/lib/wallet';
import { PstState } from '../contracts/types/types';
import { LoggerFactory, PstContract, Warp, WarpNodeFactory } from 'warp-contracts';
import fs from 'fs';
import path from 'path';
import { addFunds, mineBlock } from '../../utils/_helpers';
let contractSrc: string;
let wallet: JWKInterface;
let walletAddress: string;
let initialState: PstState;
let arweave: Arweave;
let warp: Warp;
(async () => {
// ~~ Declare variables ~~
// ~~ Initialize Arweave ~~
arweave = Arweave.init({
host: 'testnet.redstone.tools',
port: 443,
protocol: 'https',
});
// ~~ Initialize `LoggerFactory` ~~
LoggerFactory.INST.logLevel('error');
// ~~ Initialize Warp ~~
warp = WarpNodeFactory.memCached(arweave);
// ~~ Generate wallet and add some funds ~~
wallet = await arweave.wallets.generate();
walletAddress = await arweave.wallets.jwkToAddress(wallet);
await addFunds(arweave, wallet);
// ~~ Read contract source and initial state files ~~
contractSrc = fs.readFileSync(
path.join(__dirname, '../../dist/contract.js'),
'utf8'
);
const stateFromFile: PstState = JSON.parse(
fs.readFileSync(
path.join(__dirname, '../../dist/contracts/initial-state.json'),
'utf8'
)
);
// ~~ Override contract's owner address with the generated wallet address ~~
initialState = {
...stateFromFile,
...{
owner: walletAddress,
},
};
// ~~ Deploy contract ~~
const contractTxId = await warp.createContract.deploy({
wallet,
initState: JSON.stringify(initialState),
src: contractSrc,
});
// ~~ Log contract id to the console ~~
console.log(contractTxId);
//Mine block
await mineBlock(arweave);
})();
The above code will generate and fund the Arweave wallet, read, contract source and initial state files and deploy the contract to the testnet. After the contract is deployed successfully, you will be able to see the contract address in the command line.
Run the following comand:yarn ts-node src/tools/deploy-test-contract.ts

The above screenshot shows the contract is deployed successfully to redstone testnet, and the contract address is:
XeJiSHIkj0dVU7ddGtOIE8kZoQEIPcqvLS_y5KPu62w
As it is not a frontend tutorial, we've already got you covered. We will be working with Vue v.2 with typescript support.
To quickly walk you through the structure of the project:
challenge/src/main.ts — a starting point for the application.
challenge/src/pst-contract.ts — here we define Arweave and SmartWeave instances and export them.
challenge/src/deployed-contracts.ts — here we indicate deployed contract id.
challenge/src/constants.ts — all the constants (including urls).
challenge/src/assets — all the assets used in the application.
challenge/src/components — all the components that are key features of Vue to encapsulate reusable code.
challenge/src/router — router of the application build with vue-router.
Head to challenge/src/pst-contract.ts, copy the following code:
import Arweave from 'arweave';
import {
PstContract,
PstState,
Warp,
WarpNodeFactory,
LoggerFactory,
InteractionResult,
WarpWebFactory
} from 'warp-contracts';
export const arweave: Arweave = Arweave.init({
host: 'testnet.redstone.tools',
port: 443,
protocol: 'https',
});
export const warp: Warp = WarpWebFactory.memCachedBased(arweave).useArweaveGateway().build();
Head to challenge\src\deployed-contracts.ts, add the contract which is deployed successfully in step 7.

The contract address should be the contract that you deploy successfully to redstone testnet.
Head to challenge\src\constants.ts, copy the following code:
export const url = {
warpGateway: '[https://gateway.redstone.finance](https://gateway.redstone.finance)',
};
Head to challenge\src\store\index.ts,copy the following code,
import Vue from 'vue';
import Vuex from 'vuex';
import { arweave, warp } from '../pst-contract';
import { deployedContracts } from '../deployed-contracts';
import { PstState } from '@/contracts/types/types';
import { Contract } from 'warp-contracts';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
arweave,
warp,
state: {},
validity: {},
contract: null,
walletAddress: null,
},
mutations: {
setState(state, swState) {
state.state = swState;
},
setValidity(state, validity) {
state.validity = validity;
},
setContract(state, contract) {
state.contract = contract;
},
setWalletAddress(state, walletAddress) {
state.walletAddress = walletAddress;
},
},
actions: {
async loadState({ commit }) {
// ~~ Generate arweave wallet ~~
const wallet = await arweave.wallets.generate();
// ~~ Get wallet address and mint some tokens ~~
const walletAddress = await arweave.wallets.getAddress(wallet);
await arweave.api.get(`/mint/${walletAddress}/1000000000000000`);
// ~~ Connect deployed contract and wallet ~~
const contract: Contract = warp
.pst(deployedContracts.fc)
.connect(wallet);
commit('setContract', contract);
// ~~ Set the state of the contract ~~
const { state, validity } = await contract.readState();
commit('setState', state);
commit('setValidity', validity);
commit('setWalletAddress', walletAddress);
},
},
modules: {},
});
Head to challenge\src\components\Header\Header.vue, insert the following code:
const txId = await this.contract.writeInteraction({
function: 'mint',
qty: parseInt(this.$refs.balanceMint.value),
});
await this.arweave.api.get('mine');
// ~~ Set the balances by calling `currentState` method ~~
const newResult = await this.contract.currentState();
Please insert the code as the below screenshot:

Head to challenge\src\components\BalancesList\BalancesList.vue, insert the following code:
const tx = await this.contract.transfer({
target: address,
qty: parseInt(qty),
});
// ~~ Mine a block ~~
await this.arweave.api.get('mine');
// ~~ Set new balances list by calling `currentState` method
let newResult = await this.contract.currentState();
Please insert the code as the below screenshot:

By now, all source code editing is done.
Type yarn build at the command line to build the entire project, and the following screenshot shows a successful build:


Type yarn serve at the command line to run it in the development environment, and the following screenshot shows a successful run:

Open your browser and go to http://localhost:8080/,

Now open the interface, it will display the contract address, wallet address, and token balance information. At this point, you can mint some of the tokens first and then transfer them.

After some token minting and transfer, the PST token balances are updated.
Disclaimer: The content of this article is for information and communication purposes only and does not constitute any investment advice. If there are obvious errors of understanding or data, welcome feedback.
The content of this article was originally created by W3.Hitchhiker, please indicate the source if you need to reproduce.
Business cooperation: hello@w3hitchhiker.com
Official website: https://w3hitchhiker.com/
W3.Hitchhiker official Twitter: https://twitter.com/HitchhikerW3
import fs from 'fs';
import ArLocal from 'arlocal';
import Arweave from 'arweave';
import { JWKInterface } from 'arweave/node/lib/wallet';
import path from 'path';
import { addFunds, mineBlock } from '../utils/_helpers';
import {
PstContract,
PstState,
Warp,
WarpNodeFactory,
LoggerFactory,
InteractionResult,
} from 'warp-contracts';
describe('Testing the Profit Sharing Token', () => {
let contractSrc: string;
let wallet: JWKInterface;
let walletAddress: string;
let initialState: PstState;
let arweave: Arweave;
let arlocal: ArLocal;
let warp: Warp;
let pst: PstContract;
beforeAll(async () => {
// ~~ Declare all variables ~~
// ~~ Set up ArLocal and instantiate Arweave ~~
arlocal = new ArLocal(1820);
await arlocal.start();
arweave = Arweave.init({
host: 'localhost',
port: 1820,
protocol: 'http',
});
// ~~ Initialize 'LoggerFactory' ~~
LoggerFactory.INST.logLevel('error');
// ~~ Set up Warp ~~
warp = WarpNodeFactory.forTesting(arweave);
// ~~ Generate wallet and add funds ~~
wallet = await arweave.wallets.generate();
walletAddress = await arweave.wallets.jwkToAddress(wallet);
await addFunds(arweave, wallet);
// ~~ Read contract source and initial state files ~~
contractSrc = fs.readFileSync(path.join(__dirname, '../dist/contract.js'), 'utf8');
const stateFromFile: PstState = JSON.parse(
fs.readFileSync(path.join(__dirname, '../dist/contracts/initial-state.json'), 'utf8')
);
// ~~ Update initial state ~~
initialState = {
...stateFromFile,
...{
owner: walletAddress,
},
};
// ~~ Deploy contract ~~
const contractTxId = await warp.createContract.deploy({
wallet,
initState: JSON.stringify(initialState),
src: contractSrc,
});
// ~~ Connect to the pst contract ~~
pst = warp.pst(contractTxId);
pst.connect(wallet);
// ~~ Mine block ~~
await mineBlock(arweave);
});
afterAll(async () => {
// ~~ Stop ArLocal ~~
await arlocal.stop();
});
it('should read pst state and balance data', async () => {
expect(await pst.currentState()).toEqual(initialState);
expect(
(await pst.currentBalance('GH2IY_3vtE2c0KfQve9_BHoIPjZCS8s5YmSFS_fppKI'))
.balance
).toEqual(1000);
expect(
(await pst.currentBalance('33F0QHcb22W7LwWR1iRC8Az1ntZG09XQ03YWuw2ABqA'))
.balance
).toEqual(230);
});
it('should properly mint tokens', async () => {
await pst.writeInteraction({
function: 'mint',
qty: 2000,
});
await mineBlock(arweave);
expect((await pst.currentState()).balances[walletAddress]).toEqual(2000);
});
it('should properly add tokens for already existing balance', async () => {
});
it('should properly transfer tokens', async () => {
await pst.transfer({
target: 'GH2IY_3vtE2c0KfQve9_BHoIPjZCS8s5YmSFS_fppKI',
qty: 555,
});
await mineBlock(arweave);
expect((await pst.currentState()).balances[walletAddress]).toEqual(
2000 - 555
);
expect(
(await pst.currentState()).balances[
'GH2IY_3vtE2c0KfQve9_BHoIPjZCS8s5YmSFS_fppKI'
]
).toEqual(1000 + 555);
});
it('should properly view contract state', async () => {});
it('should properly perform dry write with overwritten caller', async () => {
const newWallet = await arweave.wallets.generate();
const overwrittenCaller = await arweave.wallets.jwkToAddress(newWallet);
await pst.transfer({
target: overwrittenCaller,
qty: 1000,
});
await mineBlock(arweave);
const result: InteractionResult<PstState, unknown> = await pst.dryWrite(
{
function: 'transfer',
target: 'GH2IY_3vtE2c0KfQve9_BHoIPjZCS8s5YmSFS_fppKI',
qty: 333,
},
overwrittenCaller
);
expect(result.state.balances[walletAddress]).toEqual(
2000 - 555 - 1000
);
expect(
result.state.balances['GH2IY_3vtE2c0KfQve9_BHoIPjZCS8s5YmSFS_fppKI']
).toEqual(1000 + 555 + 333);
expect(result.state.balances[overwrittenCaller]).toEqual(1000 - 333);
});
});
challenge/src/views — the view layer of the application.
import fs from 'fs';
import ArLocal from 'arlocal';
import Arweave from 'arweave';
import { JWKInterface } from 'arweave/node/lib/wallet';
import path from 'path';
import { addFunds, mineBlock } from '../utils/_helpers';
import {
PstContract,
PstState,
Warp,
WarpNodeFactory,
LoggerFactory,
InteractionResult,
} from 'warp-contracts';
describe('Testing the Profit Sharing Token', () => {
let contractSrc: string;
let wallet: JWKInterface;
let walletAddress: string;
let initialState: PstState;
let arweave: Arweave;
let arlocal: ArLocal;
let warp: Warp;
let pst: PstContract;
beforeAll(async () => {
// ~~ Declare all variables ~~
// ~~ Set up ArLocal and instantiate Arweave ~~
arlocal = new ArLocal(1820);
await arlocal.start();
arweave = Arweave.init({
host: 'localhost',
port: 1820,
protocol: 'http',
});
// ~~ Initialize 'LoggerFactory' ~~
LoggerFactory.INST.logLevel('error');
// ~~ Set up Warp ~~
warp = WarpNodeFactory.forTesting(arweave);
// ~~ Generate wallet and add funds ~~
wallet = await arweave.wallets.generate();
walletAddress = await arweave.wallets.jwkToAddress(wallet);
await addFunds(arweave, wallet);
// ~~ Read contract source and initial state files ~~
contractSrc = fs.readFileSync(path.join(__dirname, '../dist/contract.js'), 'utf8');
const stateFromFile: PstState = JSON.parse(
fs.readFileSync(path.join(__dirname, '../dist/contracts/initial-state.json'), 'utf8')
);
// ~~ Update initial state ~~
initialState = {
...stateFromFile,
...{
owner: walletAddress,
},
};
// ~~ Deploy contract ~~
const contractTxId = await warp.createContract.deploy({
wallet,
initState: JSON.stringify(initialState),
src: contractSrc,
});
// ~~ Connect to the pst contract ~~
pst = warp.pst(contractTxId);
pst.connect(wallet);
// ~~ Mine block ~~
await mineBlock(arweave);
});
afterAll(async () => {
// ~~ Stop ArLocal ~~
await arlocal.stop();
});
it('should read pst state and balance data', async () => {
expect(await pst.currentState()).toEqual(initialState);
expect(
(await pst.currentBalance('GH2IY_3vtE2c0KfQve9_BHoIPjZCS8s5YmSFS_fppKI'))
.balance
).toEqual(1000);
expect(
(await pst.currentBalance('33F0QHcb22W7LwWR1iRC8Az1ntZG09XQ03YWuw2ABqA'))
.balance
).toEqual(230);
});
it('should properly mint tokens', async () => {
await pst.writeInteraction({
function: 'mint',
qty: 2000,
});
await mineBlock(arweave);
expect((await pst.currentState()).balances[walletAddress]).toEqual(2000);
});
it('should properly add tokens for already existing balance', async () => {
});
it('should properly transfer tokens', async () => {
await pst.transfer({
target: 'GH2IY_3vtE2c0KfQve9_BHoIPjZCS8s5YmSFS_fppKI',
qty: 555,
});
await mineBlock(arweave);
expect((await pst.currentState()).balances[walletAddress]).toEqual(
2000 - 555
);
expect(
(await pst.currentState()).balances[
'GH2IY_3vtE2c0KfQve9_BHoIPjZCS8s5YmSFS_fppKI'
]
).toEqual(1000 + 555);
});
it('should properly view contract state', async () => {});
it('should properly perform dry write with overwritten caller', async () => {
const newWallet = await arweave.wallets.generate();
const overwrittenCaller = await arweave.wallets.jwkToAddress(newWallet);
await pst.transfer({
target: overwrittenCaller,
qty: 1000,
});
await mineBlock(arweave);
const result: InteractionResult<PstState, unknown> = await pst.dryWrite(
{
function: 'transfer',
target: 'GH2IY_3vtE2c0KfQve9_BHoIPjZCS8s5YmSFS_fppKI',
qty: 333,
},
overwrittenCaller
);
expect(result.state.balances[walletAddress]).toEqual(
2000 - 555 - 1000
);
expect(
result.state.balances['GH2IY_3vtE2c0KfQve9_BHoIPjZCS8s5YmSFS_fppKI']
).toEqual(1000 + 555 + 333);
expect(result.state.balances[overwrittenCaller]).toEqual(1000 - 333);
});
});
challenge/src/views — the view layer of the application.
No activity yet