Skip to content

Commit

Permalink
feat: add gift wallet support (#76)
Browse files Browse the repository at this point in the history
* feat(wip): add gift wallet support

* fix: fix gift wallet flows

* build: add missing dependency

* feat: add swap

* feat: add cloudflare resolver to config
  • Loading branch information
Cafe137 authored May 23, 2022
1 parent e0b9b72 commit 83f437a
Show file tree
Hide file tree
Showing 10 changed files with 2,094 additions and 23 deletions.
1,928 changes: 1,920 additions & 8 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,12 @@
"typescript": "~4.5.4"
},
"dependencies": {
"@ethersproject/providers": "^5.6.6",
"@koa/router": "^10.1.1",
"cross-zip": "^4.0.0",
"env-paths": "2",
"ethereumjs-wallet": "^1.0.2",
"ethers": "^5.6.6",
"fs-extra": "^10.1.0",
"js-yaml": "^4.1.0",
"koa": "^2.13.4",
Expand Down
36 changes: 36 additions & 0 deletions src/blockchain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { JsonRpcProvider } from '@ethersproject/providers'
import { BigNumber, Contract, providers, Wallet } from 'ethers'
import { bzzContractInterface } from './contract'

export async function sendNativeTransaction(
privateKey: string,
address: string,
amount: string,
providerHost: string,
): Promise<providers.TransactionResponse> {
const provider = new JsonRpcProvider(providerHost, 100)
await provider.ready
const signer = new Wallet(privateKey, provider)
const gasPrice = await signer.getGasPrice()

return signer.sendTransaction({
to: address,
value: amount,
gasPrice,
})
}

export async function sendBzzTransaction(
privateKey: string,
address: string,
amount: string,
providerHost: string,
): Promise<providers.TransactionResponse> {
const provider = new JsonRpcProvider(providerHost, 100)
await provider.ready
const signer = new Wallet(privateKey, provider)
const bzz = new Contract('0xdBF3Ea6F5beE45c02255B2c26a16F300502F68da', bzzContractInterface, signer)
const gasPrice = await signer.getGasPrice()

return bzz.transfer(address, BigNumber.from(amount), { gasPrice })
}
8 changes: 4 additions & 4 deletions src/config-yaml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ import { readFileSync, writeFileSync } from 'fs'
import { dump, FAILSAFE_SCHEMA, load } from 'js-yaml'
import { getPath } from './path'

export function readConfigYaml() {
export function readConfigYaml(): Record<string, unknown> {
const raw = readFileSync(getPath('config.yaml'), 'utf-8')
const data = load(raw, {
schema: FAILSAFE_SCHEMA,
})

return data
return data as Record<string, unknown>
}

export function writeConfigYaml(newValues: Record<string, any>) {
const data: Record<string, any> = readConfigYaml()
export function writeConfigYaml(newValues: Record<string, unknown>) {
const data = readConfigYaml()
for (const [key, value] of Object.entries(newValues)) {
data[key] = value
}
Expand Down
25 changes: 25 additions & 0 deletions src/contract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export const bzzContractInterface = [
{
type: 'function',
stateMutability: 'nonpayable',
payable: false,
outputs: [
{
type: 'bool',
name: '',
},
],
name: 'transfer',
inputs: [
{
type: 'address',
name: '_to',
},
{
type: 'uint256',
name: '_value',
},
],
constant: false,
},
]
9 changes: 2 additions & 7 deletions src/downloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { parse } from 'path'
import { promisify } from 'util'
import { logger } from './logger'
import { getPath, paths } from './path'
import { wait } from './utility'

interface DownloadOptions {
checkTarget?: string
Expand Down Expand Up @@ -47,7 +48,7 @@ export async function runDownloader(): Promise<void> {
checkTarget: 'static',
})
await ensureAsset(
`https://github.com/ethersphere/bee/releases/download/v1.5.1/bee-${platformString}-${archString}${suffixString}`,
`https://github.com/ethersphere/bee/releases/download/v1.6.0/bee-${platformString}-${archString}${suffixString}`,
`bee${suffixString}`,
{ chmod: process.platform !== 'win32' },
)
Expand Down Expand Up @@ -106,9 +107,3 @@ async function waitForAsset(path: string): Promise<void> {
reject()
})
}

async function wait(ms: number): Promise<void> {
return new Promise(resolve => {
setTimeout(resolve, ms)
})
}
8 changes: 4 additions & 4 deletions src/launcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export async function runLauncher() {
}

async function sendTransaction(address: string) {
const response = await fetch(`http://getxdai.co/${address}/0`, { method: 'POST' })
const response = await fetch(`https://onboarding.ethswarm.org/faucet/overlay/${address}`, { method: 'POST' })
const json = await response.json()

return { transaction: json.transactionHash, blockHash: json.nextBlockHashBee }
Expand All @@ -77,14 +77,14 @@ function createStubConfiguration() {
return `api-addr: 127.0.0.1:1633
debug-api-addr: 127.0.0.1:1635
debug-api-enable: true
password: Test
swap-enable: false
swap-initial-deposit: 0
mainnet: true
full-node: false
chain-enable: false
cors-allowed-origins: '*'
use-postage-snapshot: true
resolver-options: https://cloudflare-eth.com
data-dir: ${getPath('data-dir')}`
}

Expand All @@ -99,7 +99,7 @@ async function initializeBee() {

return runProcess(
getPath(getBeeExecutable()),
['init', `--config=${configPath}`],
['init', `--config=${configPath}`, `--password=Test`],
onStdout,
onStderr,
new AbortController(),
Expand All @@ -114,7 +114,7 @@ async function launchBee(abortController?: AbortController) {

return runProcess(
getPath(getBeeExecutable()),
['start', `--config=${configPath}`],
['start', `--config=${configPath}`, '--password=Test'],
onStdout,
onStderr,
abortController,
Expand Down
31 changes: 31 additions & 0 deletions src/server.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import Router from '@koa/router'
import Wallet from 'ethereumjs-wallet'
import { readFile } from 'fs/promises'
import Koa from 'koa'
import koaBodyparser from 'koa-bodyparser'
import serve from 'koa-static'
import { join } from 'path'
import { getApiKey } from './api-key'
import { sendBzzTransaction, sendNativeTransaction } from './blockchain'
import { readConfigYaml, writeConfigYaml } from './config-yaml'
import { rebuildElectronTray } from './electron'
import { createConfigFileAndAddress, createInitialTransaction, runLauncher } from './launcher'
Expand All @@ -11,6 +15,8 @@ import { subscribeLogServerRequests } from './logger'
import { getPath } from './path'
import { port } from './port'
import { getStatus } from './status'
import { swap } from './swap'
import { wait } from './utility'

export function runServer() {
const app = new Koa()
Expand Down Expand Up @@ -68,8 +74,33 @@ export function runServer() {
runLauncher()
context.body = { success: true }
})
router.post('/gift-wallet/:address', async context => {
const config = readConfigYaml()
const swapEndpoint = Reflect.get(config, 'swap-endpoint')
const privateKeyString = await getPrivateKey()
const { address } = context.params
await sendBzzTransaction(privateKeyString, address, '50000000000000000', swapEndpoint)
await wait(15000)
await sendNativeTransaction(privateKeyString, address, '1000000000000000000', swapEndpoint)
context.body = { success: true }
})
router.post('/swap', async context => {
const config = readConfigYaml()
const swapEndpoint = Reflect.get(config, 'swap-endpoint')
const privateKeyString = await getPrivateKey()
await swap(privateKeyString, context.request.body.dai, '10000', swapEndpoint)
context.body = { success: true }
})
app.use(router.routes())
app.use(router.allowedMethods())
const server = app.listen(port.value)
subscribeLogServerRequests(server)
}

async function getPrivateKey(): Promise<string> {
const v3 = await readFile(getPath(join('data-dir', 'keys', 'swarm.key')), 'utf-8')
const wallet = await Wallet.fromV3(v3, 'Test')
const privateKeyString = wallet.getPrivateKeyString()

return privateKeyString
}
64 changes: 64 additions & 0 deletions src/swap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Contract, ethers, providers } from 'ethers'

const contractInterface = [
{
inputs: [
{
internalType: 'uint256',
name: 'amountOutMin',
type: 'uint256',
},
{
internalType: 'address[]',
name: 'path',
type: 'address[]',
},
{
internalType: 'address',
name: 'to',
type: 'address',
},
{
internalType: 'uint256',
name: 'deadline',
type: 'uint256',
},
],
name: 'swapExactETHForTokens',
outputs: [
{
internalType: 'uint256[]',
name: 'amounts',
type: 'uint256[]',
},
],
stateMutability: 'payable',
type: 'function',
},
]

export async function swap(
privateKey: string,
xdai: string,
minimumBzz: string,
jsonRpcProvider: string,
): Promise<string[]> {
const provider = new providers.JsonRpcProvider(jsonRpcProvider, 100)
const signer = new ethers.Wallet(privateKey, provider)
const gasLimit = 1000000
const contract = new Contract('0x1C232F01118CB8B424793ae03F870aa7D0ac7f77', contractInterface, signer)
const WRAPPED_XDAI_CONTRACT = '0xe91d153e0b41518a2ce8dd3d7944fa863463a97d'
const BZZ_ON_XDAI_CONTRACT = '0xdbf3ea6f5bee45c02255b2c26a16f300502f68da'
const response = await contract.swapExactETHForTokens(
minimumBzz,
[WRAPPED_XDAI_CONTRACT, BZZ_ON_XDAI_CONTRACT],
await signer.getAddress(),
Date.now(),
{
value: xdai,
gasLimit,
},
)

return response
}
5 changes: 5 additions & 0 deletions src/utility.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export async function wait(ms: number): Promise<void> {
return new Promise(resolve => {
setTimeout(resolve, ms)
})
}

0 comments on commit 83f437a

Please sign in to comment.