diff --git a/functions/package.json b/functions/package.json index 79de1b65..83abac53 100644 --- a/functions/package.json +++ b/functions/package.json @@ -18,7 +18,8 @@ "firebase-admin": "10.0.0", "firebase-functions": "3.16.0", "lodash": "4.17.21", - "mailgun-js": "0.22.0" + "mailgun-js": "0.22.0", + "stripe": "8.194.0" }, "devDependencies": { "@types/mailgun-js": "0.22.12", diff --git a/functions/src/index.ts b/functions/src/index.ts index 088ca534..cd2d429a 100644 --- a/functions/src/index.ts +++ b/functions/src/index.ts @@ -5,5 +5,6 @@ admin.initializeApp() // export * from './keep-awake' export * from './place-bet' export * from './resolve-market' +export * from './stripe' export * from './sell-bet' export * from './create-contract' diff --git a/functions/src/stripe.ts b/functions/src/stripe.ts new file mode 100644 index 00000000..aac128cb --- /dev/null +++ b/functions/src/stripe.ts @@ -0,0 +1,126 @@ +import * as functions from 'firebase-functions' +import * as admin from 'firebase-admin' +import Stripe from 'stripe' + +import { payUser } from './resolve-market' + +const stripe = new Stripe(functions.config().stripe.apikey, { + apiVersion: '2020-08-27', + typescript: true, +}) + +// manage at https://dashboard.stripe.com/test/products?active=true +const manticDollarStripePrice = + admin.instanceId().app.options.projectId === 'mantic-markets' + ? { + 500: 'price_1KFQXcGdoFKoCJW770gTNBrm', + 1000: 'price_1KFQp1GdoFKoCJW7Iu0dsF65', + 2500: 'price_1KFQqNGdoFKoCJW7SDvrSaEB', + 10000: 'price_1KFQraGdoFKoCJW77I4XCwM3', + } + : { + 500: 'price_1K8W10GdoFKoCJW7KWORLec1', + 1000: 'price_1K8bC1GdoFKoCJW76k3g5MJk', + 2500: 'price_1K8bDSGdoFKoCJW7avAwpV0e', + 10000: 'price_1K8bEiGdoFKoCJW7Us4UkRHE', + } + +export const createCheckoutSession = functions + .runWith({ minInstances: 1 }) + .https.onRequest(async (req, res) => { + const userId = req.query.userId?.toString() + + const manticDollarQuantity = req.query.manticDollarQuantity?.toString() + + if (!userId) { + res.status(400).send('Invalid user ID') + return + } + + if ( + !manticDollarQuantity || + !Object.keys(manticDollarStripePrice).includes(manticDollarQuantity) + ) { + res.status(400).send('Invalid Mantic Dollar quantity') + return + } + + const referrer = + req.query.referer || req.headers.referer || 'https://mantic.markets' + + const session = await stripe.checkout.sessions.create({ + metadata: { + userId, + manticDollarQuantity, + }, + line_items: [ + { + price: + manticDollarStripePrice[ + manticDollarQuantity as unknown as keyof typeof manticDollarStripePrice + ], + quantity: 1, + }, + ], + mode: 'payment', + success_url: `${referrer}?funding-success`, + cancel_url: `${referrer}?funding-failiure`, + }) + + res.redirect(303, session.url || '') + }) + +export const stripeWebhook = functions + .runWith({ minInstances: 1 }) + .https.onRequest(async (req, res) => { + let event + + try { + event = stripe.webhooks.constructEvent( + req.rawBody, + req.headers['stripe-signature'] as string, + functions.config().stripe.webhooksecret + ) + } catch (e: any) { + console.log(`Webhook Error: ${e.message}`) + res.status(400).send(`Webhook Error: ${e.message}`) + return + } + + if (event.type === 'checkout.session.completed') { + const session = event.data.object as any + await issueMoneys(session) + } + + res.status(200).send('success') + }) + +const issueMoneys = async (session: any) => { + const { id: sessionId } = session + + const query = await firestore + .collection('stripe-transactions') + .where('sessionId', '==', sessionId) + .get() + + if (!query.empty) { + console.log('session', sessionId, 'already processed') + return + } + + const { userId, manticDollarQuantity } = session.metadata + const payout = Number.parseInt(manticDollarQuantity) + + await firestore.collection('stripe-transactions').add({ + userId, + manticDollarQuantity: payout, // save as number + sessionId, + session, + }) + + await payUser([userId, payout]) + + console.log('user', userId, 'paid M$', payout) +} + +const firestore = admin.firestore() diff --git a/functions/yarn.lock b/functions/yarn.lock index 6cec7f41..5a958ea1 100644 --- a/functions/yarn.lock +++ b/functions/yarn.lock @@ -314,7 +314,7 @@ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== -"@types/node@*", "@types/node@>=12.12.47", "@types/node@>=13.7.0": +"@types/node@*", "@types/node@>=12.12.47", "@types/node@>=13.7.0", "@types/node@>=8.1.0": version "17.0.1" resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.1.tgz#88d501e84b6185f6489ecee4ba9e8fcec7f29bb2" integrity sha512-NXKvBVUzIbs6ylBwmOwHFkZS2EXCcjnqr8ZCRNaXBkHAf+3mn/rPcJxwrzuc6movh8fxQAsUUfYklJ/EG+hZqQ== @@ -464,6 +464,14 @@ bytes@3.1.1: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.1.tgz#3f018291cb4cbad9accb6e6970bca9c8889e879a" integrity sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg== +call-bind@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -895,6 +903,11 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + ftp@~0.3.10: version "0.3.10" resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" @@ -946,6 +959,15 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-intrinsic@^1.0.2: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + get-stream@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" @@ -1018,6 +1040,18 @@ gtoken@^5.0.4: google-p12-pem "^3.0.3" jws "^4.0.0" +has-symbols@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + hash-stream-validation@^0.2.2: version "0.2.4" resolved "https://registry.yarnpkg.com/hash-stream-validation/-/hash-stream-validation-0.2.4.tgz#ee68b41bf822f7f44db1142ec28ba9ee7ccb7512" @@ -1428,6 +1462,11 @@ object-hash@^2.1.1: resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== +object-inspect@^1.9.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" + integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== + on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -1605,6 +1644,13 @@ qs@6.9.6: resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.6.tgz#26ed3c8243a431b2924aca84cc90471f35d5a0ee" integrity sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ== +qs@^6.6.0: + version "6.10.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.2.tgz#c1431bea37fc5b24c5bdbafa20f16bdf2a4b9ffe" + integrity sha512-mSIdjzqznWgfd4pMii7sHtaYF8rx8861hBO80SraY5GT0XQibWZWJSid0avzHGkDIZLImux2S5mXO0Hfct2QCw== + dependencies: + side-channel "^1.0.4" + range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" @@ -1729,6 +1775,15 @@ setprototypeof@1.2.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + signal-exit@^3.0.2: version "3.0.6" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" @@ -1822,6 +1877,14 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +stripe@8.194.0: + version "8.194.0" + resolved "https://registry.yarnpkg.com/stripe/-/stripe-8.194.0.tgz#67fc7a34260f95f9103834a1f0962d27c608cf73" + integrity sha512-iERByJUNA7sdkfQ3fD1jcrAZqPxCtTmL2EUzvHUVLXyoacDrflkq4ux5KFxYhfCIerrOAhquVj17+sBHn96/Kg== + dependencies: + "@types/node" ">=8.1.0" + qs "^6.6.0" + stubs@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/stubs/-/stubs-3.0.0.tgz#e8d2ba1fa9c90570303c030b6900f7d5f89abe5b" diff --git a/web/components/add-funds-button.tsx b/web/components/add-funds-button.tsx new file mode 100644 index 00000000..3c70604e --- /dev/null +++ b/web/components/add-funds-button.tsx @@ -0,0 +1,76 @@ +import clsx from 'clsx' +import { useState } from 'react' + +import { useUser } from '../hooks/use-user' +import { checkoutURL } from '../lib/service/stripe' +import { FundsSelector } from './yes-no-selector' + +export function AddFundsButton(props: { className?: string }) { + const { className } = props + const user = useUser() + + const [amountSelected, setAmountSelected] = useState< + 500 | 1000 | 2500 | 10000 + >(500) + + return ( + <> + + + +
+
+
Get Manifold Dollars
+ +
+ Use Manifold Dollars to trade in your favorite markets.
(Not + redeemable for cash.) +
+ +
Amount
+ + +
+
Price USD
+
+ ${Math.round(amountSelected / 100)}.00 +
+
+ +
+ + +
+ +
+
+
+
+ + ) +} diff --git a/web/components/bet-panel.tsx b/web/components/bet-panel.tsx index bf92f033..a8d52e1b 100644 --- a/web/components/bet-panel.tsx +++ b/web/components/bet-panel.tsx @@ -21,6 +21,7 @@ import { calculatePayoutAfterCorrectBet, } from '../lib/calculate' import { firebaseLogin } from '../lib/firebase/users' +import { AddFundsButton } from './add-funds-button' import { OutcomeLabel } from './outcome-label' import { AdvancedPanel } from './advanced-panel' import { Bet } from '../lib/firebase/bets' @@ -149,6 +150,7 @@ export function BetPanel(props: { contract: Contract; className?: string }) { {error} )} + {user && }
Implied probability
diff --git a/web/components/profile-menu.tsx b/web/components/profile-menu.tsx index 2c8ad379..9186568b 100644 --- a/web/components/profile-menu.tsx +++ b/web/components/profile-menu.tsx @@ -48,6 +48,10 @@ function getNavigationOptions(user: User, options: { mobile: boolean }) { name: 'Your markets', href: `/${user.username}`, }, + // { + // name: 'Add funds', + // href: '/add-funds', + // }, { name: 'Discord', href: 'https://discord.gg/eHQBNBqXuh', diff --git a/web/components/yes-no-selector.tsx b/web/components/yes-no-selector.tsx index 528b199d..9e31cdd6 100644 --- a/web/components/yes-no-selector.tsx +++ b/web/components/yes-no-selector.tsx @@ -1,5 +1,6 @@ import clsx from 'clsx' import React from 'react' +import { formatMoney } from '../lib/util/format' import { Col } from './layout/col' import { Row } from './layout/row' @@ -80,6 +81,33 @@ export function YesNoCancelSelector(props: { ) } +const fundAmounts = [500, 1000, 2500, 10000] + +export function FundsSelector(props: { + selected: 500 | 1000 | 2500 | 10000 + onSelect: (selected: 500 | 1000 | 2500 | 10000) => void + className?: string + btnClassName?: string +}) { + const { selected, onSelect, className } = props + const btnClassName = clsx('px-2 whitespace-nowrap', props.btnClassName) + + return ( + + {fundAmounts.map((amount) => ( + + ))} + + ) +} + function Button(props: { className?: string onClick?: () => void diff --git a/web/lib/service/stripe.ts b/web/lib/service/stripe.ts new file mode 100644 index 00000000..a5f6c4a0 --- /dev/null +++ b/web/lib/service/stripe.ts @@ -0,0 +1,13 @@ +import { isProd } from '../firebase/init' + +export const checkoutURL = ( + userId: string, + manticDollarQuantity: number, + referer = '' +) => { + const endpoint = isProd + ? 'https://us-central1-mantic-markets.cloudfunctions.net/createCheckoutSession' + : 'https://us-central1-dev-mantic-markets.cloudfunctions.net/createCheckoutSession' + + return `${endpoint}?userId=${userId}&manticDollarQuantity=${manticDollarQuantity}&referer=${referer}` +} diff --git a/web/pages/add-funds.tsx b/web/pages/add-funds.tsx new file mode 100644 index 00000000..0a55c364 --- /dev/null +++ b/web/pages/add-funds.tsx @@ -0,0 +1,68 @@ +import { useState } from 'react' +import { Col } from '../components/layout/col' +import { SEO } from '../components/SEO' +import { Title } from '../components/title' +import { FundsSelector } from '../components/yes-no-selector' +import { useUser } from '../hooks/use-user' +import { checkoutURL } from '../lib/service/stripe' +import Image from 'next/image' +import { Spacer } from '../components/layout/spacer' +import { Page } from '../components/page' + +export default function AddFundsPage() { + const user = useUser() + + const [amountSelected, setAmountSelected] = useState< + 500 | 1000 | 2500 | 10000 + >(500) + + return ( + + + + + + + <Image + className="block mt-6" + src="/praying-mantis-light.svg" + width={200} + height={200} + /> + + <div className="text-gray-500 mb-6"> + Use Manifold Dollars to trade in your favorite markets. <br /> (Not + redeemable for cash.) + </div> + + <div className="text-gray-500 text-sm mb-2">Amount</div> + <FundsSelector + className="max-w-md" + selected={amountSelected} + onSelect={setAmountSelected} + /> + + <div className="mt-6"> + <div className="text-gray-500 text-sm mb-1">Price USD</div> + <div className="text-xl"> + ${Math.round(amountSelected / 100)}.00 + </div> + </div> + + <form + action={checkoutURL(user?.id || '', amountSelected)} + method="POST" + className="mt-12" + > + <button + type="submit" + className="btn btn-primary w-full font-medium bg-gradient-to-r from-teal-500 to-green-500 hover:from-teal-600 hover:to-green-600" + > + Checkout + </button> + </form> + </Col> + </Col> + </Page> + ) +} diff --git a/web/public/praying-mantis-light.svg b/web/public/praying-mantis-light.svg new file mode 100644 index 00000000..cc82cd53 --- /dev/null +++ b/web/public/praying-mantis-light.svg @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + id="svg2" + xml:space="preserve" + width="2829.3333" + height="2829.3333" + viewBox="0 0 2829.3333 2829.3333" + sodipodi:docname="shutterstock_2073140717.eps"><metadata + id="metadata8"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs + id="defs6" /><sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="640" + inkscape:window-height="480" + id="namedview4" /><g + id="g10" + inkscape:groupmode="layer" + inkscape:label="ink_ext_XXXXXX" + transform="matrix(1.3333333,0,0,-1.3333333,0,2829.3333)"><g + id="g12" + transform="scale(0.1)"><path + d="m 10622.5,11248.2 c -81.6,-54.3 -178.4,-122.5 -292.3,-197.1 -114.1,-74.3 -241.9,-159.2 -384.07,-247.8 -140.7,-90.6 -296.56,-183.4 -460.04,-283.5 -81.17,-50.9 -167.57,-98.4 -254.29,-149.2 -87.66,-49.5 -174.65,-104.3 -266.68,-153.9 -91.29,-50.7 -184.14,-102.4 -278.09,-154.6 -93.94,-52.3 -191.33,-100.9 -287.65,-153.3 -192.27,-105.6 -393.64,-201 -592.62,-301 -99.96,-49 -201.33,-94.9 -301.6,-142.3 -100.32,-47.3 -200.08,-94.8 -301.41,-136.4 -100.82,-42.9 -200.55,-86.1 -298.87,-129.4 -98.47,-42.8 -198.16,-79 -294.96,-118 -97.38,-37.3 -191.91,-77.8 -286.76,-110.5 -94.68,-33.6 -187.3,-66.4 -277.42,-98.3 -45.11,-15.9 -89.61,-31.7 -133.44,-47.3 -44.37,-13.7 -88.12,-27.1 -131.09,-40.4 -86.05,-26.7 -169.14,-52.4 -248.91,-77.2 -39.96,-12.3 -79.06,-24.4 -117.3,-36.2 -38.71,-9.8 -76.56,-19.5 -113.44,-28.9 -73.75,-19.2 -143.71,-37.5 -209.45,-54.6 -65.9,-16.6 -127.23,-34.6 -184.73,-46.9 -57.61,-11.8 -110.58,-22.6 -158.43,-32.4 -191.41,-40.4 -300.86,-63.6 -301.14,-63.6 0.2,0 111.49,12.4 306.1,34.2 48.79,5.8 102.77,12.2 161.6,19.2 58.83,7.2 121.13,21.7 188.51,33.5 67.27,12.6 138.87,26.1 214.34,40.3 37.73,7.3 76.44,14.7 116.05,22.4 39.11,9.8 79.11,19.8 119.97,29.9 81.75,20.6 166.87,42 255.03,64.2 44.07,11.2 88.95,22.5 134.18,35 44.77,13.9 90.28,28.1 136.37,42.4 92.31,28.7 187.07,58.2 283.99,88.3 48.78,14.2 96.99,31.6 145.43,49.7 48.63,17.8 97.69,35.8 147.18,53.9 98.64,37.1 200.35,71.4 300.78,112.2 100.47,40.8 201.92,82.9 303.87,125.8 102.74,41.4 203.75,88.5 305.43,135.4 101.49,47.1 204.38,92.5 305.51,141.4 200,102.5 402.34,200.4 595.35,308.4 195.7,103 382.34,214.9 563.79,321.6 91.25,52.3 178.28,108.5 264.06,162.3 85.31,54.6 170.2,105.7 249.92,160 80.24,53.6 158.25,105.6 233.75,155.9 75.39,50.4 145.9,102.7 214.81,150.9 138.27,95.7 260.57,190 369.47,272.5 109.4,81.8 200.6,159.2 277.3,220.1 76,61.8 132.8,113.9 172.9,147.7 39.7,34.4 60.8,52.8 60.8,52.8 0,0 -91.6,-64.5 -251.8,-177.2" + style="fill:#668a29;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path14" /><path + d="m 15228.8,15825 c -102.2,-173.3 -176.3,-358 -218.3,-550.5 -19.6,-17.6 -37.4,-37 -53.4,-58 -163.6,59.3 -342.5,63.1 -484.2,31 54.1,715.2 441.9,1360.2 1047.5,1839.3 653.8,517 1560.1,838.1 2563.2,838 v 99.8 c -1024.4,-0.1 -1951.8,-327.4 -2625.1,-859.5 -610.2,-482.2 -1010.9,-1135.1 -1080.6,-1863.1 24.7,-17.7 47.3,-38.1 67.4,-60.7 64.3,-72.4 103.6,-167.4 103.6,-271.7 0,-169.6 -103,-315.2 -250,-377.3 -49.1,-20.7 -102.9,-32.2 -159.4,-32.2 -188.5,0 -346.9,127.6 -394.5,301 -361.9,-310.9 -784.7,-754 -1230.9,-1104.8 -123.3,-97 -259.3,-238.4 -399.4,-403.4 -69.3,-51.5 -131.7,-122.5 -178.1,-219.4 -456.2,-583.6 -916.7,-1325.1 -1094.3,-1547.3 -457.6,-216.4 -911.4,-311.1 -1384.14,-404.1 -205.43,-40.3 -414.41,-80.5 -628.86,-130 -130.39,-30.2 -262.89,-64.1 -397.74,-103.4 -167.58,-48.9 -338.83,-106.5 -514.76,-177.3 -199.85,-80.5 -405.55,-177.8 -618.36,-298.2 -39.92,-22.7 -78.24,-45.2 -117.42,-67.8 -174.61,-100.7 -341.76,-201.1 -501.61,-300.9 C 6201.64,9806 5787.19,9513.4 5425,9237.8 c -215.78,-164.2 -413.55,-322.4 -595.2,-472.1 -135.85,-111.9 -262.73,-218.9 -381.99,-320.3 0.28,0 109.73,23.2 301.14,63.6 47.85,9.8 100.82,20.6 158.43,32.4 57.5,12.3 118.83,30.3 184.73,46.9 65.74,17.1 135.7,35.4 209.45,54.6 36.88,9.4 74.73,19.1 113.44,28.9 38.24,11.8 77.34,23.9 117.3,36.2 79.77,24.8 162.86,50.5 248.91,77.2 42.97,13.3 86.72,26.7 131.09,40.4 43.83,15.6 88.33,31.4 133.44,47.3 90.12,31.9 182.74,64.7 277.42,98.3 94.85,32.7 189.38,73.2 286.76,110.5 96.8,39 196.49,75.2 294.96,118 98.32,43.3 198.05,86.5 298.87,129.4 101.33,41.6 201.09,89.1 301.41,136.4 100.27,47.4 201.64,93.3 301.6,142.3 198.98,100 400.35,195.4 592.62,301 96.32,52.4 193.71,101 287.65,153.3 93.95,52.2 186.8,103.9 278.09,154.6 92.03,49.6 179.02,104.4 266.68,153.9 86.72,50.8 173.12,98.3 254.29,149.2 163.48,100.1 319.34,192.9 460.04,283.5 142.17,88.6 269.97,173.5 384.07,247.8 113.9,74.6 210.7,142.8 292.3,197.1 160.2,112.7 251.8,177.2 251.8,177.2 0,0 -21.1,-18.4 -60.8,-52.8 -40.1,-33.8 -96.9,-85.9 -172.9,-147.7 -76.7,-60.9 -167.9,-138.3 -277.3,-220.1 -108.9,-82.5 -231.2,-176.8 -369.47,-272.5 -68.91,-48.2 -139.42,-100.5 -214.81,-150.9 -75.5,-50.3 -153.51,-102.3 -233.75,-155.9 -79.72,-54.3 -164.61,-105.4 -249.92,-160 -85.78,-53.8 -172.81,-110 -264.06,-162.3 -181.45,-106.7 -368.09,-218.6 -563.79,-321.6 -193.01,-108 -395.35,-205.9 -595.35,-308.4 -101.13,-48.9 -204.02,-94.3 -305.51,-141.4 -101.68,-46.9 -202.69,-94 -305.43,-135.4 -101.95,-42.9 -203.4,-85 -303.87,-125.8 -100.43,-40.8 -202.14,-75.1 -300.78,-112.2 -49.49,-18.1 -98.55,-36.1 -147.18,-53.9 -48.44,-18.1 -96.65,-35.5 -145.43,-49.7 -96.92,-30.1 -191.68,-59.6 -283.99,-88.3 -46.09,-14.3 -91.6,-28.5 -136.37,-42.4 -45.23,-12.5 -90.11,-23.8 -134.18,-35 -88.16,-22.2 -173.28,-43.6 -255.03,-64.2 -40.86,-10.1 -80.86,-20.1 -119.97,-29.9 -39.61,-7.7 -78.32,-15.1 -116.05,-22.4 -75.47,-14.2 -147.07,-27.7 -214.34,-40.3 -67.38,-11.8 -129.68,-26.3 -188.51,-33.5 -58.83,-7 -112.81,-13.4 -161.6,-19.2 -194.61,-21.8 -305.9,-34.2 -306.1,-34.2 -663.4,-564.2 -1086.87,-950 -1454.8,-983.5 72.89,-128.8 730.04,15.1 1664.29,339.7 141.8,49.3 289.85,102.7 443.36,160 51.41,19.1 103.44,38.8 156.02,58.8 52.23,-252.7 180.66,-592.2 394.45,-911.7 142.27,-212.7 291.06,-397.8 427.27,-538.4 l -3.79,-2.3 C 5872.3,6432.5 3914.65,3342.3 4005.23,3206.8 c 90.63,-135.4 2195.32,2735.1 2397.7,2870.4 202.34,135.4 292.97,354.9 202.3,490.5 -12.93,19.3 -29.18,35.6 -47.61,49.4 -63.56,215.9 -210.74,513.2 -415.86,819.8 -123.63,184.9 -164.26,536.4 -199.73,855.9 569.34,235 1177.93,507.5 1784.73,805.2 87.85,-408.4 251.83,-803.5 371.33,-959.7 97.34,-159.8 600.11,-435.7 1207.54,-515.4 253.75,-33.3 490.97,-43.5 686.6,-33.8 l -0.75,-4.3 c -31.64,-241.4 1000.52,-3751 1162.12,-3772.2 161.7,-21.2 -608.5,3453.9 -576.8,3695.4 31.7,241.3 -73.7,454.2 -235.3,475.4 -23.1,3.1 -46,1.6 -68.6,-3.1 -204.1,94.5 -524.66,179.9 -890.44,227.9 -242.62,31.8 -570.78,272.4 -853.59,468.9 40.08,153.4 74.26,327.7 98.67,514 17.73,135 28.87,265.3 33.98,387.3 141.41,76.3 281.53,153.6 419.73,231.8 190.35,107.7 373.91,215.7 551.95,323.5 148.87,90.2 293.4,180.2 433.4,269.7 798.4,510.8 1451.5,1007.5 1939,1436.7 86.8,-272.8 194.7,-506.2 280,-617.7 97.3,-159.7 600.1,-435.6 1207.6,-515.3 253.7,-33.4 490.9,-43.5 686.6,-33.8 l -0.8,-4.4 c -31.7,-241.4 1000.5,-3750.9 1162.2,-3772.1 161.6,-21.2 -608.6,3453.9 -576.9,3695.3 31.7,241.4 -73.7,454.3 -235.3,475.5 -23.1,3 -46,1.5 -68.5,-3.2 -204.2,94.5 -524.8,179.9 -890.5,227.9 -242.6,31.9 -570.8,272.5 -853.6,469 40.1,153.4 74.2,327.7 98.7,513.9 17.4,132.5 28.4,260.4 33.7,380.4 0.9,20.9 1.6,41.5 2.2,61.9 102.2,140.7 160.6,254.7 171.7,334.9 146.6,101.3 290.1,206.2 425.5,312.6 94.5,74.3 186.3,144.3 274.9,210.9 323.5,243.3 601.7,440.3 795.1,626.5 239.4,-336.9 587.3,-500.1 789.4,-364.5 144.8,97.1 192.2,434.7 126.7,751.4 151.3,60.1 258.4,207.6 258.4,380.3 0,226.1 -183.3,409.5 -409.5,409.5 -53.4,0 -104.3,-10.6 -151.1,-29.2 41.3,147 102.8,289 182.7,424.4 236.5,401.5 634.1,745.9 1137.5,989.6 503.4,243.7 1111.9,386.5 1767.9,386.5 v 99.8 c -893.9,-0.1 -1703.5,-259 -2292.2,-680.9 -294.2,-210.9 -533.4,-462.9 -699.2,-744.3" + style="fill:#bfe142;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path16" /><path + d="m 14096.4,15241.1 c -14.7,4.9 -30.1,8.2 -46.6,8.2 -81.6,0 -147.8,-66.2 -147.8,-148 0,-4.7 0.9,-9.3 1.4,-14 7.1,-75 69.6,-133.8 146.4,-133.8 81.7,0 148,66.2 148,147.8 0,65.3 -42.6,120.1 -101.4,139.8" + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path18" /><path + d="m 14139.5,14802.3 c -153.4,0 -277.7,222.5 -277.7,222.5 0,0 15.3,27.3 41.6,62.5 -0.5,4.7 -1.4,9.3 -1.4,14 0,81.8 66.2,148 147.8,148 16.5,0 31.9,-3.3 46.6,-8.2 14.1,3.6 28.4,6.2 43.1,6.2 153.3,0 277.6,-222.5 277.6,-222.5 0,0 -124.3,-222.5 -277.6,-222.5 z m 238.4,499.7 c -67.2,48.3 -149.3,77.1 -238.4,77.1 -226.1,0 -409.5,-183.4 -409.5,-409.5 0,-37.7 5.5,-73.9 15,-108.5 47.6,-173.4 206,-301 394.5,-301 56.5,0 110.3,11.5 159.4,32.2 147,62.1 250,207.7 250,377.3 0,104.3 -39.3,199.3 -103.6,271.7 -20.1,22.6 -42.7,43 -67.4,60.7" + style="fill:#d4e61d;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path20" /><path + d="m 14139.5,15247.3 c -14.7,0 -29,-2.6 -43.1,-6.2 58.8,-19.7 101.4,-74.5 101.4,-139.8 0,-81.6 -66.3,-147.8 -148,-147.8 -76.8,0 -139.3,58.8 -146.4,133.8 -26.3,-35.2 -41.6,-62.5 -41.6,-62.5 0,0 124.3,-222.5 277.7,-222.5 153.3,0 277.6,222.5 277.6,222.5 0,0 -124.3,222.5 -277.6,222.5" + style="fill:#293519;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path22" /><path + d="m 5843.09,10913.9 1.01,4.3 c 120.16,-58.3 516.88,-510.3 835.31,-813.7 159.85,99.8 327,200.2 501.61,300.9 -364.5,119.2 -1220.16,989.6 -1442,982.7 -18.82,13.2 -39.37,23.6 -61.83,29.8 -157.03,43.9 -337.35,-110.8 -402.78,-345.3 -27.89,-100 -231.91,-1141.6 -444.61,-2306.9 181.65,149.7 379.42,307.9 595.2,472.1 221.99,884.7 395.16,1594 418.09,1676.1" + style="fill:#96d42f;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path24" /><path + d="m 4332.03,5382.5 c 76.92,-21.4 425.39,1249.4 768.63,2579.1 -153.51,-57.3 -301.56,-110.7 -443.36,-160 C 4437.54,6545 4260.27,5402.5 4332.03,5382.5" + style="fill:#96d42f;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path26" /><path + d="m 8577.89,11337.9 2.89,-3.4 c 23.01,-24.8 112.07,-126.4 248.52,-282.4 214.45,49.5 423.43,89.7 628.86,130 -243.75,309.5 -410.74,517.5 -444.37,553.8 -165.47,178.5 -396.56,233.5 -516.09,122.7 -17.15,-15.9 -30.67,-34.5 -41.37,-54.8 -133.2,-63.4 -338.01,-599.7 -539.53,-1032.4 175.93,70.8 347.18,128.4 514.76,177.3 58.36,189 109.38,339.9 146.33,389.2" + style="fill:#96d42f;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path28" /><path + d="m 12455.6,7107.7 c 78.1,72.5 -1347,1953 -2389,3295.7 -140,-89.5 -284.53,-179.5 -433.4,-269.7 1121.5,-1276.9 2742.7,-3099.8 2822.4,-3026" + style="fill:#96d42f;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path30" /><path + d="m 14364.1,11431.3 c 253.8,-33.3 491,-43.5 686.6,-33.8 l -0.7,-4.4 c -31.7,-241.4 1000.4,-3750.9 1162.1,-3772.1 161.7,-21.2 -608.5,3453.9 -576.8,3695.3 31.7,241.4 -73.7,454.3 -235.3,475.5 -23.1,3.1 -46.1,1.6 -68.6,-3.1 -204.2,94.5 -524.7,179.9 -890.5,227.9 -242.6,31.9 -570.8,272.5 -853.6,469 40.1,153.3 74.3,327.7 98.7,513.9 27.3,207.8 39,404.5 37.1,576.4 -88.6,-66.6 -180.4,-136.6 -274.9,-210.9 -135.4,-106.4 -278.9,-211.3 -425.5,-312.6 -11.1,-80.2 -69.5,-194.2 -171.7,-334.9 -0.6,-20.4 -1.3,-41 -2.2,-61.9 90.5,-311.4 213.1,-585.2 307.7,-709 97.4,-159.7 600.2,-435.6 1207.6,-515.3" + style="fill:#96d42f;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path32" /></g></g></svg>