working transaction creation

This commit is contained in:
samepant 2021-04-11 22:28:30 -04:00
parent 7cab03c653
commit d9d2e32cf8
9 changed files with 229 additions and 105 deletions

View File

@ -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>
);
);
};

View File

@ -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>

View File

@ -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>
);

View File

@ -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),

View File

@ -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
}

View 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();
});
}

View 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();
});
}

View File

@ -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>
);

View File

@ -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>