Skip to content

Commit 9a2d126

Browse files
authored
add asm transfer example (#527)
* add asm transfer example * add asm workflow * Update solana-asm.yml * add Rust unit tests * add ts tests * remove unused const * clean up * biome fixes * update heyAyushh/setup-solana version in ci * add continue-on-error * add assert Update test.ts
1 parent 6063401 commit 9a2d126

14 files changed

Lines changed: 2058 additions & 0 deletions

File tree

.github/workflows/solana-asm.yml

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
name: ASM
2+
3+
on:
4+
schedule:
5+
- cron: "0 0 * * *"
6+
push:
7+
branches:
8+
- main
9+
pull_request:
10+
types: [opened, synchronize, reopened]
11+
branches:
12+
- main
13+
14+
env:
15+
MAX_JOBS: 64
16+
MIN_PROJECTS_PER_JOB: 4
17+
MIN_PROJECTS_FOR_MATRIX: 4
18+
19+
jobs:
20+
changes:
21+
runs-on: ubuntu-latest
22+
permissions:
23+
pull-requests: read
24+
outputs:
25+
changed_projects: ${{ steps.analyze.outputs.changed_projects }}
26+
total_projects: ${{ steps.analyze.outputs.total_projects }}
27+
matrix: ${{ steps.matrix.outputs.matrix }}
28+
steps:
29+
- uses: actions/checkout@v4
30+
- uses: dorny/paths-filter@v3
31+
id: changes
32+
if: github.event_name == 'pull_request'
33+
with:
34+
list-files: shell
35+
filters: |
36+
asm:
37+
- added|modified: '**/asm/**'
38+
workflow:
39+
- added|modified: '.github/workflows/solana-asm.yml'
40+
- name: Analyze Changes
41+
id: analyze
42+
run: |
43+
# Generate ignore pattern, excluding comments
44+
ignore_pattern=$(grep -v '^#' .github/.ghaignore | grep -v '^$' | tr '\n' '|' | sed 's/|$//')
45+
echo "Ignore pattern: $ignore_pattern"
46+
47+
function get_projects() {
48+
find . -type d -name "asm" | grep -vE "$ignore_pattern" | sort
49+
}
50+
51+
# Determine which projects to build and test
52+
if [[ "${{ github.event_name }}" == "push" || "${{ github.event_name }}" == "schedule" || "${{ steps.changes.outputs.workflow }}" == "true" ]]; then
53+
projects=$(get_projects)
54+
elif [[ "${{ steps.changes.outputs.asm }}" == "true" ]]; then
55+
changed_files=(${{ steps.changes.outputs.asm_files }})
56+
projects=$(for file in "${changed_files[@]}"; do dirname "${file}" | grep asm | sed 's#/asm/.*#/asm#g'; done | grep -vE "$ignore_pattern" | sort -u)
57+
else
58+
projects=""
59+
fi
60+
61+
# Output project information
62+
if [[ -n "$projects" ]]; then
63+
echo "Projects to build and test"
64+
echo "$projects"
65+
total_projects=$(echo "$projects" | wc -l)
66+
echo "Total projects: $total_projects"
67+
echo "total_projects=$total_projects" >> $GITHUB_OUTPUT
68+
echo "changed_projects=$(echo "$projects" | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT
69+
else
70+
echo "No projects to build and test."
71+
echo "total_projects=0" >> $GITHUB_OUTPUT
72+
echo "changed_projects=[]" >> $GITHUB_OUTPUT
73+
fi
74+
- name: Generate matrix
75+
id: matrix
76+
run: |
77+
total_projects=${{ steps.analyze.outputs.total_projects }}
78+
max_jobs=${{ env.MAX_JOBS }}
79+
min_projects_per_job=${{ env.MIN_PROJECTS_PER_JOB }}
80+
min_projects_for_matrix=${{ env.MIN_PROJECTS_FOR_MATRIX }}
81+
82+
if [ "$total_projects" -lt "$min_projects_for_matrix" ]; then
83+
echo "matrix=[0]" >> $GITHUB_OUTPUT
84+
else
85+
projects_per_job=$(( (total_projects + max_jobs - 1) / max_jobs ))
86+
projects_per_job=$(( projects_per_job > min_projects_per_job ? projects_per_job : min_projects_per_job ))
87+
num_jobs=$(( (total_projects + projects_per_job - 1) / projects_per_job ))
88+
89+
indices=$(seq 0 $(( num_jobs - 1 )))
90+
echo "matrix=[$(echo $indices | tr ' ' ',')]" >> $GITHUB_OUTPUT
91+
fi
92+
93+
build-and-test:
94+
needs: changes
95+
if: needs.changes.outputs.total_projects != '0'
96+
runs-on: ubuntu-latest
97+
strategy:
98+
fail-fast: false
99+
matrix:
100+
index: ${{ fromJson(needs.changes.outputs.matrix) }}
101+
name: build-and-test-group-${{ matrix.index }}
102+
outputs:
103+
failed_projects: ${{ steps.set-failed.outputs.failed_projects }}
104+
steps:
105+
- uses: actions/checkout@v4
106+
- name: Use Node.js
107+
uses: actions/setup-node@v4
108+
with:
109+
node-version: "lts/*"
110+
check-latest: true
111+
- name: Setup build environment
112+
id: setup
113+
run: |
114+
# Create the build and test function
115+
cat << 'EOF' > build_and_test.sh
116+
function build_and_test() {
117+
local project=$1
118+
local solana_version=$2
119+
echo "Building and Testing $project with Solana $solana_version"
120+
cd "$project" || return 1
121+
122+
# Install dependencies
123+
if ! pnpm install --frozen-lockfile; then
124+
echo "::error::pnpm install failed for $project"
125+
echo "$project: pnpm install failed with $solana_version" >> $GITHUB_WORKSPACE/failed_projects.txt
126+
cd - > /dev/null
127+
return 1
128+
fi
129+
130+
# Build and Test
131+
if ! pnpm build-and-test; then
132+
echo "::error::build-and-test failed for $project"
133+
echo "$project: build-and-test failed with $solana_version" >> $GITHUB_WORKSPACE/failed_projects.txt
134+
cd - > /dev/null
135+
return 1
136+
fi
137+
138+
# Run Rust unit tests
139+
if [ -d "program" ]; then
140+
echo "Running Rust unit tests for $project"
141+
if ! cargo test --manifest-path=./program/Cargo.toml; then
142+
echo "::error::Rust unit tests failed for $project"
143+
echo "$project: Rust unit tests failed with $solana_version" >> $GITHUB_WORKSPACE/failed_projects.txt
144+
cd - > /dev/null
145+
return 1
146+
fi
147+
fi
148+
149+
echo "Build and tests succeeded for $project with $solana_version version."
150+
cd - > /dev/null
151+
return 0
152+
}
153+
154+
function process_projects() {
155+
local solana_version=$1
156+
157+
readarray -t all_projects < <(echo '${{ needs.changes.outputs.changed_projects }}' | jq -r '.[]?')
158+
start_index=$(( ${{ matrix.index }} * ${{ env.MIN_PROJECTS_PER_JOB }} ))
159+
end_index=$(( start_index + ${{ env.MIN_PROJECTS_PER_JOB }} ))
160+
end_index=$(( end_index > ${{ needs.changes.outputs.total_projects }} ? ${{ needs.changes.outputs.total_projects }} : end_index ))
161+
162+
echo "Projects to build and test in this job"
163+
for i in $(seq $start_index $(( end_index - 1 ))); do
164+
echo "${all_projects[$i]}"
165+
done
166+
167+
failed=false
168+
for i in $(seq $start_index $(( end_index - 1 ))); do
169+
echo "::group::Building and testing ${all_projects[$i]}"
170+
if ! build_and_test "${all_projects[$i]}" "$solana_version"; then
171+
failed=true
172+
fi
173+
echo "::endgroup::"
174+
done
175+
176+
return $([ "$failed" = true ] && echo 1 || echo 0)
177+
}
178+
EOF
179+
180+
# Make the script executable
181+
chmod +x build_and_test.sh
182+
183+
# Install pnpm
184+
npm install --global pnpm
185+
186+
# Install sbpf assembler
187+
cargo install --git https://github.com/blueshift-gg/sbpf.git
188+
- name: Setup Solana Stable
189+
uses: heyAyushh/setup-solana@v5.9
190+
with:
191+
solana-cli-version: stable
192+
- name: Build and Test with Stable
193+
run: |
194+
source build_and_test.sh
195+
solana -V
196+
rustc -V
197+
process_projects "stable"
198+
# continue-on-error because the beta channel may not have a valid release
199+
# (e.g. v4.0 returns 404 from release.anza.xyz). This is an upstream issue
200+
# with heyAyushh/setup-solana — beta setup clears the stable install first.
201+
- name: Setup Solana Beta
202+
continue-on-error: true
203+
uses: heyAyushh/setup-solana@v5.9
204+
with:
205+
solana-cli-version: beta
206+
- name: Build and Test with Beta
207+
continue-on-error: true
208+
run: |
209+
source build_and_test.sh
210+
solana -V
211+
rustc -V
212+
process_projects "beta"
213+
214+
- name: Set failed projects output
215+
id: set-failed
216+
if: failure()
217+
run: |
218+
if [ -f "$GITHUB_WORKSPACE/failed_projects.txt" ]; then
219+
failed_projects=$(cat $GITHUB_WORKSPACE/failed_projects.txt | jq -R -s -c 'split("\n")[:-1]')
220+
echo "failed_projects=$failed_projects" >> $GITHUB_OUTPUT
221+
else
222+
echo "failed_projects=[]" >> $GITHUB_OUTPUT
223+
fi
224+
225+
summary:
226+
needs: [changes, build-and-test]
227+
if: always()
228+
runs-on: ubuntu-latest
229+
steps:
230+
- uses: actions/checkout@v4
231+
- name: Create job summary
232+
run: |
233+
echo "## ASM Workflow Summary" >> $GITHUB_STEP_SUMMARY
234+
echo "- Total projects: ${{ needs.changes.outputs.total_projects }}" >> $GITHUB_STEP_SUMMARY
235+
236+
# List all processed projects
237+
echo "<details>" >> $GITHUB_STEP_SUMMARY
238+
echo "<summary>Projects processed (click to expand)</summary>" >> $GITHUB_STEP_SUMMARY
239+
echo "" >> $GITHUB_STEP_SUMMARY
240+
echo '${{ needs.changes.outputs.changed_projects }}' | jq -r '.[]' | while read project; do
241+
echo "- $project" >> $GITHUB_STEP_SUMMARY
242+
done
243+
echo "" >> $GITHUB_STEP_SUMMARY
244+
echo "</details>" >> $GITHUB_STEP_SUMMARY
245+
246+
# Report build and test results
247+
if [[ "${{ needs.build-and-test.result }}" == "failure" ]]; then
248+
echo "## :x: Build or tests failed" >> $GITHUB_STEP_SUMMARY
249+
echo "<details>" >> $GITHUB_STEP_SUMMARY
250+
echo "<summary>Failed projects (click to expand)</summary>" >> $GITHUB_STEP_SUMMARY
251+
echo "" >> $GITHUB_STEP_SUMMARY
252+
failed_projects='${{ needs.build-and-test.outputs.failed_projects }}'
253+
if [[ -n "$failed_projects" ]]; then
254+
echo "$failed_projects" | jq -r '.[]' | while IFS=: read -r project failure_reason; do
255+
echo "- **$project**" >> $GITHUB_STEP_SUMMARY
256+
echo " - Failure reason: $failure_reason" >> $GITHUB_STEP_SUMMARY
257+
done
258+
else
259+
echo "No failed projects reported. This might indicate an unexpected error in the workflow." >> $GITHUB_STEP_SUMMARY
260+
fi
261+
echo "" >> $GITHUB_STEP_SUMMARY
262+
echo "</details>" >> $GITHUB_STEP_SUMMARY
263+
elif [[ "${{ needs.build-and-test.result }}" == "success" ]]; then
264+
echo "## :white_check_mark: All builds and tests passed" >> $GITHUB_STEP_SUMMARY
265+
else
266+
echo "## :warning: Build and test job was skipped or canceled" >> $GITHUB_STEP_SUMMARY
267+
fi

Cargo.lock

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ members = [
4343
"basics/transfer-sol/native/program",
4444
"basics/transfer-sol/pinocchio/program",
4545
"basics/transfer-sol/anchor/programs/*",
46+
"basics/transfer-sol/asm",
4647

4748
# tokens
4849
"tokens/token-2022/mint-close-authority/native/program",
@@ -89,3 +90,4 @@ solana-keypair = "3.0.1"
8990
solana-pubkey = "3.0.0"
9091
solana-transaction = "3.0.1"
9192
solana-native-token = "3.0.0"
93+
solana-address = "2.1.0"

basics/transfer-sol/asm/.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
build/**/*
2+
deploy/**/*
3+
node_modules
4+
.sbpf
5+
.DS_Store
6+
.vscode
7+
keypair.json
8+
package-lock.json
9+
test-ledger
10+
yarn.lock
11+
target

basics/transfer-sol/asm/Cargo.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[package]
2+
name = "asm"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
8+
[dev-dependencies]
9+
litesvm.workspace = true
10+
solana-instruction.workspace = true
11+
solana-address.workspace = true
12+
solana-keypair.workspace = true
13+
solana-native-token.workspace = true
14+
solana-pubkey.workspace = true
15+
solana-transaction.workspace = true
16+
solana-system-interface.workspace = true
17+
18+
[features]
19+
test-sbf = []

basics/transfer-sol/asm/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# asm
2+
3+
Created with [sbpf](https://github.com/blueshift-gg/sbpf)

basics/transfer-sol/asm/cicd.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/bin/bash
2+
3+
# This script is for quick building & deploying of the program.
4+
# It also serves as a reference for the commands used for building & deploying Solana programs.
5+
# Run this bad boy with "bash cicd.sh" or "./cicd.sh"
6+
7+
sbpf build --deploy-dir ./tests/fixtures/
8+
solana program deploy ./tests/fixtures/transfer-sol-cpi.so
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"type": "module",
3+
"scripts": {
4+
"test": "pnpm ts-mocha -p ./tsconfig.json -t 1000000 ./tests/test.ts",
5+
"build-and-test": "sbpf build --deploy-dir ./tests/fixtures && pnpm test",
6+
"build": "sbpf build",
7+
"deploy": "solana program deploy ./program/target/so/program.so"
8+
},
9+
"dependencies": {
10+
"@solana/web3.js": "^1.47.3",
11+
"buffer-layout": "^1.2.2",
12+
"fs": "^0.0.1-security"
13+
},
14+
"devDependencies": {
15+
"@types/bn.js": "^5.1.0",
16+
"@types/chai": "^4.3.1",
17+
"@types/mocha": "^9.1.1",
18+
"chai": "^4.3.4",
19+
"mocha": "^9.0.3",
20+
"solana-bankrun": "^0.3.0",
21+
"ts-mocha": "^10.0.0",
22+
"typescript": "^4.3.5"
23+
}
24+
}

0 commit comments

Comments
 (0)