Skip to content

Commit

Permalink
basic implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
fuxingloh committed Mar 18, 2024
1 parent 41ff787 commit 2324d9a
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 105 deletions.
1 change: 1 addition & 0 deletions .idea/dictionaries/fuxing.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

58 changes: 42 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,52 @@

```typescript
import { SolanaContainer, StartedSolanaContainer } from 'solana-testcontainers';
import { createPublicClient, http, PublicClient } from 'viem';
import { solana } from 'viem/chains';

let container: StartedSolanaContainer;

beforeAll(async () => {
container = await new SolanaContainer().start();
import { Connection, PublicKey } from '@solana/web3.js';

describe('SolanaContainer', () => {
let container: StartedSolanaContainer;
let connection: Connection;

beforeAll(async () => {
container = await new SolanaContainer().start();
connection = new Connection(container.getHostRpcEndpoint(), {
commitment: 'processed',
wsEndpoint: container.getHostWsEndpoint(),
});
});

afterAll(async () => {
await container.stop();
});

it('should get processed block height', async () => {
const blockHeight = await connection.getBlockHeight('processed');
expect(blockHeight).toBeGreaterThanOrEqual(0);
});

it('should fund address with 5129000000 lamports with confirmation', async () => {
const publicKey = new PublicKey('Emp8JcXpFnCXzdWBC3ChRPtNQHiiQW6kr61wopT3hbNL');
const lamports = 5_129_000_000;

const block = await connection.getLatestBlockhash('processed');
const signature = await connection.requestAirdrop(publicKey, lamports);
await connection.confirmTransaction({ signature, ...block }, 'processed');

const balance = await connection.getBalance(publicKey, 'processed');
expect(balance).toStrictEqual(lamports);
});
});
```

afterAll(async () => {
await container.stop();
});
## Motivation

it('should rpc(eth_blockNumber) via viem', async () => {
const client = createPublicClient({ chain: solana, transport: http(container.getHostRpcUrl()) });
This library creates a Docker image that isolates the toolchain for Solana from the host system.
This is particularly useful for language-agnostic development and parallelization of systems.

const blockNumber = await client.getBlockNumber();
expect(blockNumber).toStrictEqual(BigInt(0));
});
```
The default [solanalabs/solana](https://hub.docker.com/r/solanalabs/solana) is an optimized image,
when used on a host system that does not support AVX, it will fail with the following error:
Incompatible CPU detected: missing AVX support.
Please build from source on the target.

## License

Expand Down
2 changes: 1 addition & 1 deletion packages/solana-container/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ RUN apt update && \

COPY --from=builder /workspace/solana/target/release/solana-test-validator /bin/solana-test-validator

CMD ["solana-test-validator", "--log"]
CMD ["solana-test-validator"]
6 changes: 5 additions & 1 deletion packages/solana-testcontainers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ export class StartedSolanaContainer extends AbstractStartedContainer {
super(startedTestContainer);
}

getHostRpcUrl(): string {
getHostRpcEndpoint(): string {
return `http://${this.getHost()}:${this.getMappedPort(8899)}`;
}

getHostWsEndpoint(): string {
return `ws://${this.getHost()}:${this.getMappedPort(8900)}`;
}
}
54 changes: 20 additions & 34 deletions packages/solana-testcontainers/index.unit.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,42 @@
import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
import { createPublicClient, http, PublicClient } from 'viem';
import { solana } from 'viem/chains';
import waitForExpect from 'wait-for-expect';
import { Connection, PublicKey } from '@solana/web3.js';

import { SolanaContainer, StartedSolanaContainer } from './index';

describe('default container', () => {
describe('SolanaContainer', () => {
let container: StartedSolanaContainer;
let connection: Connection;

beforeAll(async () => {
container = await new SolanaContainer().start();
connection = new Connection(container.getHostRpcEndpoint(), {
commitment: 'processed',
wsEndpoint: container.getHostWsEndpoint(),
});
});

afterAll(async () => {
await container.stop();
});

it('should expose host rpc url', async () => {
expect(container.getHostRpcUrl()).toMatch(/http:\/\/localhost:\d+/);
it('should expose host rpc endpoint', async () => {
expect(container.getHostRpcEndpoint()).toMatch(/http:\/\/localhost:\d+/);
});

it('should rpc(eth_blockNumber) via viem', async () => {
const client = createPublicClient({
chain: solana,
transport: http(container.getHostRpcUrl()),
});

const blockNumber = await client.getBlockNumber();
expect(blockNumber).toBeGreaterThanOrEqual(0n);
it('should get processed block height', async () => {
const blockHeight = await connection.getBlockHeight('processed');
expect(blockHeight).toBeGreaterThanOrEqual(0);
});
});

describe('auto mining container 2000ms interval', () => {
let container: StartedSolanaContainer;
let client: PublicClient;

beforeAll(async () => {
container = await new SolanaContainer().withMiningInterval(2000).start();
client = createPublicClient({
chain: solana,
transport: http(container.getHostRpcUrl()),
});
});
it('should fund address with 5129000000 lamports with confirmation', async () => {
const publicKey = new PublicKey('Emp8JcXpFnCXzdWBC3ChRPtNQHiiQW6kr61wopT3hbNL');
const lamports = 5_129_000_000;

afterAll(async () => {
await container.stop();
});
const block = await connection.getLatestBlockhash('processed');
const signature = await connection.requestAirdrop(publicKey, lamports);
await connection.confirmTransaction({ signature, ...block }, 'processed');

it('should auto mine block', async () => {
await waitForExpect(async () => {
const blockNumber = await client.getBlockNumber();
expect(blockNumber).toBeGreaterThan(1n);
}, 6000);
const balance = await connection.getBalance(publicKey, 'processed');
expect(balance).toStrictEqual(lamports);
});
});
2 changes: 1 addition & 1 deletion packages/solana-testcontainers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@
"preset": "@workspace/jest-preset"
},
"dependencies": {
"@solana/web3.js": "1.91.1",
"testcontainers": "^10.7.2"
},
"devDependencies": {
"@solana/web3.js": "1.91.1",
"@workspace/jest-preset": "workspace:*",
"@workspace/tsconfig": "workspace:*",
"solana-container": "workspace:*"
Expand Down
Loading

0 comments on commit 2324d9a

Please sign in to comment.