working transaction creation
This commit is contained in:
parent
7cab03c653
commit
d9d2e32cf8
@ -1,15 +1,22 @@
|
||||
import StackableContainer from "../layout/StackableContainer";
|
||||
|
||||
export default (props) => (
|
||||
<StackableContainer lessPadding>
|
||||
<h2>Holdings</h2>
|
||||
<StackableContainer lessPadding lessMargin>
|
||||
<span>1,021,010 ATOM</span>
|
||||
export default (props) => {
|
||||
const uatomToAtom = (uatom) => {
|
||||
if (uatom === 0) return 0;
|
||||
return uatom / 1000000;
|
||||
};
|
||||
|
||||
return (
|
||||
<StackableContainer lessPadding>
|
||||
<h2>Holdings</h2>
|
||||
<StackableContainer lessPadding lessMargin>
|
||||
<span>{props.holdings} ATOM</span>
|
||||
</StackableContainer>
|
||||
<style jsx>{`
|
||||
span {
|
||||
text-align: center;
|
||||
}
|
||||
`}</style>
|
||||
</StackableContainer>
|
||||
<style jsx>{`
|
||||
span {
|
||||
text-align: center;
|
||||
}
|
||||
`}</style>
|
||||
</StackableContainer>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,48 +1,43 @@
|
||||
import { abbreviateLongString } from "../../lib/displayHelpers";
|
||||
import StackableContainer from "../layout/StackableContainer";
|
||||
|
||||
const uatomToAtom = (uatom) => {
|
||||
return uatom / 1000000;
|
||||
};
|
||||
export default (props) => (
|
||||
<StackableContainer lessPadding lessMargin>
|
||||
<ul className="meta-data">
|
||||
{props.tx.amount && (
|
||||
{props.tx.msgs && (
|
||||
<li>
|
||||
<label>Amount:</label>
|
||||
<div>{props.tx.amount} ATOM</div>
|
||||
<div>{uatomToAtom(props.tx.msgs[0].value.amount[0].amount)} ATOM</div>
|
||||
</li>
|
||||
)}
|
||||
{props.tx.to && (
|
||||
{props.tx.msgs && (
|
||||
<li>
|
||||
<label>To:</label>
|
||||
<div title={props.tx.to}>
|
||||
{props.abbreviate ? abbreviateLongString(props.tx.to) : props.tx.to}
|
||||
<div title={props.tx.msgs[0].value.toAddress}>
|
||||
{props.tx.abbreviate
|
||||
? abbreviateLongString(props.tx.msgs[0].value.toAddress)
|
||||
: props.tx.msgs[0].value.toAddress}
|
||||
</div>
|
||||
</li>
|
||||
)}
|
||||
|
||||
{props.tx.txHash && (
|
||||
<li>
|
||||
<label>Tx Hash:</label>
|
||||
<div title={props.tx.txHash}>
|
||||
{abbreviateLongString(props.tx.txHash)}
|
||||
</div>
|
||||
</li>
|
||||
)}
|
||||
{props.tx.status && (
|
||||
<li>
|
||||
<label>Status:</label>
|
||||
<div>{props.tx.status}</div>
|
||||
</li>
|
||||
)}
|
||||
<li>
|
||||
<label>Status:</label>
|
||||
<div>{props.tx.status || "signing in progress"}</div>
|
||||
</li>
|
||||
{props.tx.fee && (
|
||||
<li>
|
||||
<label>Fee:</label>
|
||||
<div>{props.tx.fee}</div>
|
||||
<label>Gas:</label>
|
||||
<div>{props.tx.fee.gas} UATOM</div>
|
||||
</li>
|
||||
)}
|
||||
{props.tx.time && (
|
||||
{props.tx.memo && (
|
||||
<li>
|
||||
<label>Date:</label>
|
||||
<div>{props.tx.time}</div>
|
||||
<label>Memo:</label>
|
||||
<div>{props.tx.memo}</div>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import axios from "axios";
|
||||
import { coins } from "@cosmjs/launchpad";
|
||||
import React from "react";
|
||||
import { withRouter } from "next/router";
|
||||
|
||||
@ -6,33 +7,6 @@ import Button from "../../components/inputs/Button";
|
||||
import Input from "../../components/inputs/Input";
|
||||
import StackableContainer from "../layout/StackableContainer";
|
||||
|
||||
const baseTX = {
|
||||
type: "cosmos-sdk/StdTx",
|
||||
value: {
|
||||
msg: [
|
||||
{
|
||||
type: "cosmos-sdk/MsgSend",
|
||||
value: {
|
||||
from_address: "",
|
||||
to_address: "",
|
||||
amount: [
|
||||
{
|
||||
denom: "uatom",
|
||||
amount: "0",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
fee: {
|
||||
amount: [],
|
||||
gas: "0",
|
||||
},
|
||||
signatures: null,
|
||||
memo: "",
|
||||
},
|
||||
};
|
||||
|
||||
class TransactionForm extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -54,10 +28,29 @@ class TransactionForm extends React.Component {
|
||||
};
|
||||
|
||||
createTransaction = (toAddress, amount, gas) => {
|
||||
baseTX.value.msg[0].value.to_address = toAddress;
|
||||
baseTX.value.msg[0].value.from_address = this.props.address;
|
||||
baseTX.value.msg[0].value.amount[0].amount = amount.toString();
|
||||
baseTX.value.fee.gas = gas.toString();
|
||||
const msgSend = {
|
||||
fromAddress: this.props.address,
|
||||
toAddress: toAddress,
|
||||
amount: coins(amount * 1000000, "uatom"),
|
||||
};
|
||||
const msg = {
|
||||
typeUrl: "/cosmos.bank.v1beta1.MsgSend",
|
||||
value: msgSend,
|
||||
};
|
||||
const gasLimit = gas;
|
||||
const fee = {
|
||||
amount: coins(2000, "uatom"),
|
||||
gas: gasLimit.toString(),
|
||||
};
|
||||
|
||||
return {
|
||||
accountNumber: this.props.accountOnChain.accountNumber,
|
||||
sequence: this.props.accountOnChain.sequence,
|
||||
chainId: "cosmoshub-4",
|
||||
msgs: [msg],
|
||||
fee: fee,
|
||||
memo: this.state.memo,
|
||||
};
|
||||
return baseTX;
|
||||
};
|
||||
|
||||
@ -70,13 +63,11 @@ class TransactionForm extends React.Component {
|
||||
this.state.gas
|
||||
);
|
||||
|
||||
const res = await axios.post("/api/transaction", {
|
||||
unsignedJson: JSON.stringify(tx),
|
||||
multiAddress: this.props.multiAddress,
|
||||
});
|
||||
|
||||
const dataJSON = JSON.stringify(tx);
|
||||
const res = await axios.post("/api/transaction", { dataJSON });
|
||||
const { transactionID } = res.data;
|
||||
this.props.router.push(
|
||||
`${this.props.multiAddress}/transaction/${res.data}`
|
||||
`${this.props.address}/transaction/${transactionID}`
|
||||
);
|
||||
} else {
|
||||
this.setState({ addressError: "Use a valid cosmos-hub address" });
|
||||
@ -86,6 +77,9 @@ class TransactionForm extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<StackableContainer lessPadding>
|
||||
<button className="remove" onClick={this.props.closeForm}>
|
||||
✕
|
||||
</button>
|
||||
<h2>Create New transaction</h2>
|
||||
<div className="form-item">
|
||||
<Input
|
||||
@ -93,6 +87,7 @@ class TransactionForm extends React.Component {
|
||||
name="toAddress"
|
||||
value={this.state.toAddress}
|
||||
onChange={this.handleChange}
|
||||
error={this.state.addressError}
|
||||
placeholder="cosmos1fjrzd7ycxzse05zme3r2zqwpsvcrskv80wj82h"
|
||||
/>
|
||||
</div>
|
||||
@ -107,21 +102,23 @@ class TransactionForm extends React.Component {
|
||||
</div>
|
||||
<div className="form-item">
|
||||
<Input
|
||||
label="Gas"
|
||||
label="Gas (UATOM)"
|
||||
name="gas"
|
||||
type="number"
|
||||
value={this.state.gas}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
label="Create Transaction"
|
||||
onClick={() => {
|
||||
this.props.router.push(
|
||||
`${this.props.address}/transaction/${"kjas-q981-asda-143d"}`
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<div className="form-item">
|
||||
<Input
|
||||
label="Memo"
|
||||
name="memo"
|
||||
type="text"
|
||||
value={this.state.memo}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
</div>
|
||||
<Button label="Create Transaction" onClick={this.handleCreate} />
|
||||
<style jsx>{`
|
||||
p {
|
||||
margin-top: 15px;
|
||||
@ -129,6 +126,17 @@ class TransactionForm extends React.Component {
|
||||
.form-item {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
button.remove {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
color: white;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
}
|
||||
`}</style>
|
||||
</StackableContainer>
|
||||
);
|
||||
|
||||
@ -12,6 +12,7 @@ export default (props) => (
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.container:first-child {
|
||||
@ -19,7 +20,7 @@ export default (props) => (
|
||||
}
|
||||
|
||||
.base {
|
||||
max-width: 700px;
|
||||
max-width: 750px;
|
||||
background: #62145f;
|
||||
box-shadow: 0px 28px 80px rgba(0, 0, 0, 0.07),
|
||||
0px 12.7134px 39.2617px rgba(0, 0, 0, 0.0519173),
|
||||
|
||||
@ -13,22 +13,13 @@ type SourceAddress {
|
||||
}
|
||||
|
||||
type Transaction {
|
||||
multisigId: Multisig!
|
||||
signatures: [Signature] @relation
|
||||
toAddress: String!
|
||||
fromAddress: String!
|
||||
amount: Float!
|
||||
threshold: Int!
|
||||
txHash: String
|
||||
broadcastDate: String
|
||||
gas: Int!
|
||||
fee: Float
|
||||
JSON: String
|
||||
dataJSON: String
|
||||
}
|
||||
|
||||
type Signature {
|
||||
transaction: Transaction! @relation
|
||||
JSON: String!
|
||||
dataJSON: String!
|
||||
sourceAddress: SourceAddress @relation
|
||||
}
|
||||
|
||||
|
||||
32
pages/api/transaction/[transactionID].js
Normal file
32
pages/api/transaction/[transactionID].js
Normal file
@ -0,0 +1,32 @@
|
||||
import faunadb from "faunadb";
|
||||
|
||||
export default async function (req, res) {
|
||||
return new Promise(async (resolve) => {
|
||||
switch (req.method) {
|
||||
case "GET":
|
||||
const q = faunadb.query;
|
||||
const client = new faunadb.Client({
|
||||
secret: process.env.FAUNADB_SECRET,
|
||||
});
|
||||
try {
|
||||
const {
|
||||
query: { transactionID },
|
||||
} = req;
|
||||
console.log("Function `getTransactionByID` invoked", transactionID);
|
||||
const faunaRes = await client.query(
|
||||
q.Get(q.Ref(q.Collection("Transaction"), transactionID))
|
||||
);
|
||||
console.log("success", faunaRes);
|
||||
res.status(200).send(faunaRes);
|
||||
return resolve();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
res.status(400).send(err.message);
|
||||
return resolve();
|
||||
}
|
||||
}
|
||||
// no route matched
|
||||
res.status(405).end();
|
||||
return resolve();
|
||||
});
|
||||
}
|
||||
33
pages/api/transaction/index.js
Normal file
33
pages/api/transaction/index.js
Normal file
@ -0,0 +1,33 @@
|
||||
import faunadb from "faunadb";
|
||||
|
||||
export default async function (req, res) {
|
||||
return new Promise(async (resolve) => {
|
||||
switch (req.method) {
|
||||
case "POST":
|
||||
const q = faunadb.query;
|
||||
const client = new faunadb.Client({
|
||||
secret: process.env.FAUNADB_SECRET,
|
||||
});
|
||||
try {
|
||||
const data = req.body;
|
||||
console.log("Function `createTransaction` invoked", data);
|
||||
const tx = {
|
||||
data: data,
|
||||
};
|
||||
const faunaRes = await client.query(
|
||||
q.Create(q.Collection("Transaction"), tx)
|
||||
);
|
||||
console.log("success", faunaRes);
|
||||
res.status(200).send({ transactionID: faunaRes.ref.id });
|
||||
return resolve();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
res.status(400).send(err.message);
|
||||
return resolve();
|
||||
}
|
||||
}
|
||||
// no route matched
|
||||
res.status(405).end();
|
||||
return resolve();
|
||||
});
|
||||
}
|
||||
@ -1,7 +1,8 @@
|
||||
import React, { useState } from "react";
|
||||
import { StargateClient } from "@cosmjs/stargate";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import ConnectWallet from "../../../components/forms/ConnectWallet";
|
||||
import Button from "../../../components/inputs/Button";
|
||||
import MultisigHoldings from "../../../components/dataViews/MultisigHoldings";
|
||||
import MultisigMembers from "../../../components/dataViews/MultisigMembers";
|
||||
import Page from "../../../components/layout/Page";
|
||||
@ -9,6 +10,17 @@ import StackableContainer from "../../../components/layout/StackableContainer";
|
||||
import TransactionForm from "../../../components/forms/TransactionForm";
|
||||
import TransactionList from "../../../components/dataViews/TransactionList";
|
||||
|
||||
export async function getServerSideProps(context) {
|
||||
const client = await StargateClient.connect("143.198.6.14:26657");
|
||||
const multisigAddress = context.params.address;
|
||||
const holdings = await client.getBalance(multisigAddress, "uatom");
|
||||
const accountOnChain = await client.getAccount(multisigAddress);
|
||||
const id = await client.getChainId();
|
||||
return {
|
||||
props: { accountOnChain, holdings: holdings.amount / 1000000 },
|
||||
};
|
||||
}
|
||||
|
||||
const multipage = (props) => {
|
||||
const [showTxForm, setShowTxForm] = useState(false);
|
||||
const router = useRouter();
|
||||
@ -18,19 +30,36 @@ const multipage = (props) => {
|
||||
<StackableContainer base>
|
||||
<StackableContainer>
|
||||
<label>Multisig Address</label>
|
||||
<h1>cosmos1fjrzd7ycxzse05zme3r2zqwpsvcrskv80wj82h</h1>
|
||||
<h1>{address}</h1>
|
||||
</StackableContainer>
|
||||
{showTxForm ? (
|
||||
<TransactionForm address={address} />
|
||||
<TransactionForm
|
||||
address={address}
|
||||
accountOnChain={props.accountOnChain}
|
||||
holdings={props.holdings}
|
||||
closeForm={() => {
|
||||
setShowTxForm(false);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div className="interfaces">
|
||||
<div className="col-1">
|
||||
<MultisigMembers address={address} />
|
||||
<TransactionList address={address} />
|
||||
<MultisigHoldings holdings={props.holdings} />
|
||||
</div>
|
||||
<div className="col-2">
|
||||
<MultisigHoldings address={address} />
|
||||
<ConnectWallet onConnect={setShowTxForm} />
|
||||
<StackableContainer lessPadding>
|
||||
<h2>New transaction</h2>
|
||||
<p>
|
||||
Once a transaction is created, it can be signed by the
|
||||
multisig members, and then broadcast.
|
||||
</p>
|
||||
<Button
|
||||
label="Create Transaction"
|
||||
onClick={() => {
|
||||
setShowTxForm(true);
|
||||
}}
|
||||
/>
|
||||
</StackableContainer>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@ -42,7 +71,7 @@ const multipage = (props) => {
|
||||
margin-top: 50px;
|
||||
}
|
||||
.col-1 {
|
||||
flex: 2;
|
||||
flex: 1;
|
||||
padding-right: 50px;
|
||||
}
|
||||
.col-2 {
|
||||
@ -52,6 +81,9 @@ const multipage = (props) => {
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
}
|
||||
p {
|
||||
margin-top: 15px;
|
||||
}
|
||||
`}</style>
|
||||
</Page>
|
||||
);
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import faunadb from "faunadb";
|
||||
|
||||
import Page from "../../../../components/layout/Page";
|
||||
import StackableContainer from "../../../../components/layout/StackableContainer";
|
||||
import TransactionInfo from "../../../../components/dataViews/TransactionInfo";
|
||||
@ -15,15 +17,38 @@ const dummyTX = {
|
||||
type: "send",
|
||||
};
|
||||
|
||||
const transactionPage = ({ transaction, multi }) => {
|
||||
const txInfo = (transaction && JSON.parse(transaction.unsigned)) || null;
|
||||
export async function getServerSideProps(context) {
|
||||
const q = faunadb.query;
|
||||
const client = new faunadb.Client({
|
||||
secret: process.env.FAUNADB_SECRET,
|
||||
});
|
||||
const transactionID = context.params.transactionID;
|
||||
let transactionJSON = "";
|
||||
try {
|
||||
console.log("Function `getTransactionByID` invoked", transactionID);
|
||||
const faunaRes = await client.query(
|
||||
q.Get(q.Ref(q.Collection("Transaction"), transactionID))
|
||||
);
|
||||
console.log("success", faunaRes);
|
||||
transactionJSON = faunaRes.data.dataJSON;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
return {
|
||||
props: { transactionJSON },
|
||||
};
|
||||
}
|
||||
|
||||
const transactionPage = ({ transactionJSON }) => {
|
||||
const txInfo = (transactionJSON && JSON.parse(transactionJSON)) || null;
|
||||
console.log(txInfo);
|
||||
return (
|
||||
<Page>
|
||||
<StackableContainer base>
|
||||
<StackableContainer>
|
||||
<h1>In Progress Transaction</h1>
|
||||
</StackableContainer>
|
||||
<TransactionInfo tx={dummyTX} />
|
||||
<TransactionInfo tx={txInfo} />
|
||||
<TransactionSigning />
|
||||
</StackableContainer>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user