deploy smart contract  บน Solana ทำยังไง
Tags: solana, smartcontract, crypto
Published: 2021-08-08 14:09:41

เราสามารถ deploy program (smart contract) บน Solana ได้โดยใช้ [command line](https://docs.solana.com/cli/deploy-a-program)

บทความนี้จะมาใช้ [web3.js](https://solana-labs.github.io/solana-web3.js/) ในการ deploy smart contract บน Solana

บทความนี้เราจะข้ามกระบวนการสร้าง program (smart contract) จึงจะสมมุติว่าเราได้ program file .so มาแล้ว (Solana เรียก smart contract ว่า program ดังนั้นหากบทความนี้ใช้คำว่า program ก็หมายถึง smart contract นั่นเอง)

เพื่อที่จะ deploy program ได้เราจะต้องใช้บัญชีทั้งหมด 3 บัญชี
1. **Payer Account**  : บัญชีที่จะเอาไว้จ่ายค่าทำธุรกรรมที่เกิดขึ้นจากการ deploy program ขึ้นบน Solana
1. **Program Account** : บัญชีที่เอาไว้เก็บ program (file .so) เนื่องจาก program ใน Solana ต้องมีที่อยู่เลยต้องผูกไว้กับสักบัญชี
1. **App Account** : บัญชีนี้เป็นบัญชีผู้ใช้ทั่วไปที่จะเป็นคนใช้งาน program ซึ่งเอาไว้เก็บข้อมูลที่จำเป็นสำหรับ dApp นั่นเอง

note:  
- พื้นที่เก็บข้อมูลบน Solana คือการ[เช่า](https://docs.solana.com/developing/programming-model/accounts#rent)ไม่ใช่การจ่ายครั้งเดียว และ App Account จำเป็นต้องมี [SOL](https://dex.projectserum.com/#/market/HWHvQhFmJB3NUcu1aihKmrKegfVxBEHzwVX6yZCKEsi1) เอาไว้ใช้ค่าธรรมเนียมในการเช่าเก็บข้อมูล
- หากจะ deploy ไป mainnet โดยปกติแล้ว Payer Account จำเป็นต้องมี SOL ไว้จ่ายค่าทำธุกรรมด้วยเช่นกัน

**ขั้นแรก**
```js
const web3 = require('@solana/web3.js');
const { Connection, Keypair, clusterApiUrl } = web3

const payerAccount = new Keypair()
const programAccount = new Keypair()
const appAccount = new Keypair()

const conn = new Connection(clusterApiUrl('devnet'))
await conn.requestAirdrop(payerAccount.publicKey, 9000000000)
```
ทำการสร้างบัญชีไว้ 3 บัญชี (payerAccount, programAccount, appAccount) เตรียมไว้เพื่อใช้งาน ถ้ามี Account เดิมที่จะใช้อยู่แล้วสามารถใช้บัญชีเดิมของเราได้ โดยเรียกใช้ฟังก์ชั่น [Keypair.fromSeed](https://solana-labs.github.io/solana-web3.js/classes/Keypair.html#fromSeed) แล้วใส่ seed หรือ mnemonic phrase ของเราเข้าไป หรือในกรณีที่จะใช้แค่ publickey เราสามารถเรียกใช้ [new PublicKey("your public key")](https://solana-labs.github.io/solana-web3.js/classes/PublicKey.html) ได้เช่นเดียวกัน

เนื่องจากเรารันอยู่บน [devnet](https://docs.solana.com/clusters#example-solana-command-line-configuration) เราสามารถเสกเงินเข้าบัญชี payerAccount ด้วยการ `requestAirdrop` ด้านบนเราใส่เข้าไป 9 SOL ( 1 SOL = 1000000000 Lamports) เพื่อเอาไว้จ่ายค่าธุรกรรมที่เกิดขึ้นในการ deploy


**ขั้นสอง**
```js
 const program = await fs.readFile('/path/to/smartcontract.so')
 await web3.BpfLoader.load(
             conn, 
             payerAccount, 
             programAccount, 
             program, 
             web3.BPF_LOADER_PROGRAM_ID)
```
ทำการอัพโหลด program (ไฟล์นามสกุล .so ที่ทำการ build มาได้จาก rust) 
ไปฝากไว้ที่ programAccount ณ ตอนที่อัพโหลดบน devnet บัญชี payerAccount จ่ายค่าธรรมเนียมในการอัพโหลดไป 610488960 lamports (0.61048896 SOL)


**ขั้นสาม**
```js
const transaction = new web3.Transaction().add(
    web3.SystemProgram.createAccount({
        fromPubkey: payerAccount.publicKey,
        newAccountPubkey: appAccount.publicKey,
        lamports: 2000000000,
        space: 1000,
        programId,
    })
)
await web3.sendAndConfirmTransaction(conn, transaction, [payerAccount, appAccount], {
    commitment: 'singleGossip',
    preflightCommitment: 'singleGossip',
})
```
ในขึ้นตอนนี้เราจะทำการ deploy ตัว program (smart contract) ของเราไปยัง appAccount ซึ่งจะจัดเก็บข้อมูลที่จำเป็นสำหรับ dApp ไว้ที่นี่ด้วย จะสังเกตว่าระบุว่าจะ[เช่า](https://docs.solana.com/developing/programming-model/accounts#rent)พื้นที่จัดเก็บข้อมูลคือ 1000 bytes 
พร้อมทั้งทำการฝากเงินเข้าบัญชี appAccount เป็นจำนวน 2000000000 lamports (2 SOL) แน่นอนว่าทำการหักจากบัญชี payerAccount พร้อมทั้งค่าธรรมเนียมในการทำธุรกรรมครั้งนี้ 10000 lamports (0.00001 SOL)
programId คือที่อยู่ของ smart contract ที่เราอัพโหลดขึ้นไป นั่นก็คือ publickey ของ programAccount


เย้ program ของเราสามารถใช้งานได้แล้ว เรามาลองทำการเรียกใช้ program (smart contract) ของบัญชี appAccount กัน

```js
const msg = "hello solana AnuchiO here".padEnd(1000);
const data = Buffer.from(msg, 'utf8');
const instruction = new web3.TransactionInstruction({
    keys: [{ pubkey: appAccount.publicKey, isSigner: false, isWritable: true }],
    programId,
    data,
})
const signature = await web3.sendAndConfirmTransaction(
    conn,
    new web3.Transaction().add(instruction),
    [payerAccount],
    { commitment: 'singleGossip', preflightCommitment: 'singleGossip' },
);

console.log({ signature })
```
ทำการสร้างข้อความ "hello solana AnuchiO here" พร้อม padding คือใส่ข้อความว่างๆไป ให้ข้อความทั้งหมดยาว 1000 สิ่งนี้จำเป็นเพราะใน ฝัน program (smart contract) ที่เขียนด้วย rust จะไม่สามารถ deserialize (อ่านค่า) ข้อมูลได้ถ้าขนาดของข้อมูลไม่ครบ 
จากนั้นก็สร้าง instruction บอกว่าเราอยากจะเรียก programId นี้ใน appAccount นี้ โดยอยากเขียนข้อความลงไป (isWritable: true) และข้อความที่จะเขียนก็คือ data นั่นเอง

แล้วก็ทำการส่งคำขอทำธุรกรรม sendAndConfirmTransaction โดยบอกว่าคนที่ขอทำธุรกรรมในครั้งนี้คือ [payerAccount] นั่นเอง
ซึ่งการทำธุรกรรมครั้งนี้เสียค่าธรรมเนียมไป 5000 lamports (0.000005 SOL) ถูกมากมาก 


เพียงเท่านี้เราก็สามารถ deploy program บน Solana และสามารถเรียกใช้งานได้แล้ว
Source Code อยู่ที่ GitHub: AnuchitO https://github.com/AnuchitO/solana-deploy